luci-app-mwan3: convert to JS 5174/head
authorFlorian Eckert <fe@dev.tdt.de>
Fri, 2 Jul 2021 13:23:39 +0000 (15:23 +0200)
committerFlorian Eckert <fe@dev.tdt.de>
Fri, 9 Jul 2021 06:38:11 +0000 (08:38 +0200)
Signed-off-by: Florian Eckert <fe@dev.tdt.de>
33 files changed:
applications/luci-app-mwan3/Makefile
applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/mwan3.css [new file with mode: 0644]
applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/globals.js [new file with mode: 0644]
applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/interface.js [new file with mode: 0644]
applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/member.js [new file with mode: 0644]
applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/notify.js [new file with mode: 0644]
applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/policy.js [new file with mode: 0644]
applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/network/rule.js [new file with mode: 0644]
applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/detail.js [new file with mode: 0644]
applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/diagnostics.js [new file with mode: 0644]
applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/overview.js [new file with mode: 0644]
applications/luci-app-mwan3/htdocs/luci-static/resources/view/mwan3/status/troubleshooting.js [new file with mode: 0644]
applications/luci-app-mwan3/htdocs/luci-static/resources/view/status/include/90_mwan3.js [new file with mode: 0644]
applications/luci-app-mwan3/luasrc/controller/mwan3.lua [deleted file]
applications/luci-app-mwan3/luasrc/model/cbi/mwan/globalsconfig.lua [deleted file]
applications/luci-app-mwan3/luasrc/model/cbi/mwan/interface.lua [deleted file]
applications/luci-app-mwan3/luasrc/model/cbi/mwan/interfaceconfig.lua [deleted file]
applications/luci-app-mwan3/luasrc/model/cbi/mwan/member.lua [deleted file]
applications/luci-app-mwan3/luasrc/model/cbi/mwan/memberconfig.lua [deleted file]
applications/luci-app-mwan3/luasrc/model/cbi/mwan/notify.lua [deleted file]
applications/luci-app-mwan3/luasrc/model/cbi/mwan/policy.lua [deleted file]
applications/luci-app-mwan3/luasrc/model/cbi/mwan/policyconfig.lua [deleted file]
applications/luci-app-mwan3/luasrc/model/cbi/mwan/rule.lua [deleted file]
applications/luci-app-mwan3/luasrc/model/cbi/mwan/ruleconfig.lua [deleted file]
applications/luci-app-mwan3/luasrc/view/admin_status/index/mwan.htm [deleted file]
applications/luci-app-mwan3/luasrc/view/mwan/overview_status_interface.htm [deleted file]
applications/luci-app-mwan3/luasrc/view/mwan/status_detail.htm [deleted file]
applications/luci-app-mwan3/luasrc/view/mwan/status_diagnostics.htm [deleted file]
applications/luci-app-mwan3/luasrc/view/mwan/status_interface.htm [deleted file]
applications/luci-app-mwan3/luasrc/view/mwan/status_troubleshooting.htm [deleted file]
applications/luci-app-mwan3/root/usr/libexec/luci-mwan3 [new file with mode: 0755]
applications/luci-app-mwan3/root/usr/share/luci/menu.d/luci-app-mwan3.json [new file with mode: 0644]
applications/luci-app-mwan3/root/usr/share/rpcd/acl.d/luci-app-mwan3.json

index f53fa13f0c1e46186efcc610d2965d7932fd4056..8edba3ccd52a534c6c0c232b1e9ec3f523a6f63d 100644 (file)
@@ -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 (file)
index 0000000..ebe7764
--- /dev/null
@@ -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 (file)
index 0000000..b8c12c0
--- /dev/null
@@ -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 (file)
index 0000000..3615d22
--- /dev/null
@@ -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 (file)
index 0000000..c49cc6e
--- /dev/null
@@ -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 (file)
index 0000000..ed27535
--- /dev/null
@@ -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 (file)
index 0000000..d39eb3b
--- /dev/null
@@ -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 (file)
index 0000000..7602017
--- /dev/null
@@ -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 (file)
index 0000000..552b132
--- /dev/null
@@ -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 (file)
index 0000000..71bee68
--- /dev/null
@@ -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 (file)
index 0000000..14e0b02
--- /dev/null
@@ -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 (file)
index 0000000..6446125
--- /dev/null
@@ -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 (file)
index 0000000..0fc1155
--- /dev/null
@@ -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 (file)
index 1fb9083..0000000
+++ /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 (file)
index ec4085e..0000000
+++ /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 (file)
index 6e34311..0000000
+++ /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 (file)
index ea07bd4..0000000
+++ /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 (file)
index 5c3d0c1..0000000
+++ /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 (file)
index 3464ebf..0000000
+++ /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 (file)
index ff1d338..0000000
+++ /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 (file)
index 48a4dcc..0000000
+++ /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 (file)
index 8e5a3fa..0000000
+++ /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 (file)
index 1a97d40..0000000
+++ /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 (file)
index eca5395..0000000
+++ /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 (file)
index e4b3c06..0000000
+++ /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 (file)
index b3210ee..0000000
+++ /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 (file)
index 77fce3f..0000000
+++ /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 (file)
index b08f1d1..0000000
+++ /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 (file)
index 962cde5..0000000
+++ /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 (file)
index a20516b..0000000
+++ /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 (executable)
index 0000000..8db3e47
--- /dev/null
@@ -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 (file)
index 0000000..e646155
--- /dev/null
@@ -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"
+               }
+       }
+}
index 539ed0fb90ae6ce50875fba10dd47d8b338de869..91dd225358a37ac67ecf237778e44feb0c6a22c6 100644 (file)
@@ -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" ]
                }
        }