diff options
Diffstat (limited to 'applications/luci-app-firewall/htdocs')
4 files changed, 272 insertions, 26 deletions
diff --git a/applications/luci-app-firewall/htdocs/luci-static/resources/tools/firewall.js b/applications/luci-app-firewall/htdocs/luci-static/resources/tools/firewall.js index 198528aaa5..e983035b3d 100644 --- a/applications/luci-app-firewall/htdocs/luci-static/resources/tools/firewall.js +++ b/applications/luci-app-firewall/htdocs/luci-static/resources/tools/firewall.js @@ -308,5 +308,139 @@ return L.Class.extend({ else /* if (x == 'DROP') */ return _('Discard input'); } + }, + + addDSCPOption: function(s, is_target) { + var o = s.taboption(is_target ? 'general' : 'advanced', form.Value, is_target ? 'set_dscp' : 'dscp', + is_target ? _('DSCP mark') : _('Match DSCP'), + is_target ? _('Apply the given DSCP class or value to established connections.') : _('Matches traffic carrying the specified DSCP marking.')); + + o.modalonly = true; + o.rmempty = !is_target; + o.placeholder = _('any'); + + if (is_target) + o.depends('target', 'DSCP'); + + o.value('CS0'); + o.value('CS1'); + o.value('CS2'); + o.value('CS3'); + o.value('CS4'); + o.value('CS5'); + o.value('CS6'); + o.value('CS7'); + o.value('BE'); + o.value('AF11'); + o.value('AF12'); + o.value('AF13'); + o.value('AF21'); + o.value('AF22'); + o.value('AF23'); + o.value('AF31'); + o.value('AF32'); + o.value('AF33'); + o.value('AF41'); + o.value('AF42'); + o.value('AF43'); + o.value('EF'); + o.validate = function(section_id, value) { + if (value == '') + return is_target ? _('DSCP mark required') : true; + + if (!is_target) + value = String(value).replace(/^!\s*/, ''); + + var m = value.match(/^(?:CS[0-7]|BE|AF[1234][123]|EF|(0x[0-9a-f]{1,2}|[0-9]{1,2}))$/); + + if (!m || (m[1] != null && +m[1] > 0x3f)) + return _('Invalid DSCP mark'); + + return true; + }; + + return o; + }, + + addMarkOption: function(s, is_target) { + var o = s.taboption(is_target ? 'general' : 'advanced', form.Value, + (is_target > 1) ? 'set_xmark' : (is_target ? 'set_mark' : 'mark'), + (is_target > 1) ? _('XOR mark') : (is_target ? _('Set mark') : _('Match mark')), + (is_target > 1) ? _('Apply a bitwise XOR of the given value and the existing mark value on established connections. Format is value[/mask]. If a mask is specified then those bits set in the mask are zeroed out.') : + (is_target ? _('Set the given mark value on established connections. Format is value[/mask]. If a mask is specified then only those bits set in the mask are modified.') : + _('Matches a specific firewall mark or a range of different marks.'))); + + o.modalonly = true; + o.rmempty = true; + + if (is_target > 1) + o.depends('target', 'MARK_XOR'); + else if (is_target) + o.depends('target', 'MARK_SET'); + + o.validate = function(section_id, value) { + if (value == '') + return is_target ? _('Valid firewall mark required') : true; + + if (!is_target) + value = String(value).replace(/^!\s*/, ''); + + var m = value.match(/^(0x[0-9a-f]{1,8}|[0-9]{1,10})(?:\/(0x[0-9a-f]{1,8}|[0-9]{1,10}))?$/i); + + if (!m || +m[1] > 0xffffffff || (m[2] != null && +m[2] > 0xffffffff)) + return _('Expecting: %s').format(_('valid firewall mark')); + + return true; + }; + + return o; + }, + + addLimitOption: function(s) { + var o = s.taboption('advanced', form.Value, 'limit', + _('Limit matching'), + _('Limits traffic matching to the specified rate.')); + + o.modalonly = true; + o.rmempty = true; + o.placeholder = _('unlimited'); + o.value('10/second'); + o.value('60/minute'); + o.value('3/hour'); + o.value('500/day'); + o.validate = function(section_id, value) { + if (value == '') + return true; + + var m = String(value).toLowerCase().match(/^(?:0x[0-9a-f]{1,8}|[0-9]{1,10})\/([a-z]+)$/), + u = ['second', 'minute', 'hour', 'day'], + i = 0; + + if (m) + for (i = 0; i < u.length; i++) + if (u[i].indexOf(m[1]) == 0) + break; + + if (!m || i >= u.length) + return _('Invalid limit value'); + + return true; + }; + + return o; + }, + + addLimitBurstOption: function(s) { + var o = s.taboption('advanced', form.Value, 'limit_burst', + _('Limit burst'), + _('Maximum initial number of packets to match: this number gets recharged by one every time the limit specified above is not reached, up to this number.')); + + o.modalonly = true; + o.rmempty = true; + o.placeholder = '5'; + o.datatype = 'uinteger'; + o.depends({ limit: null, '!reverse': true }); + + return o; } }); diff --git a/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/forwards.js b/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/forwards.js index dc2249275e..500e68fb17 100644 --- a/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/forwards.js +++ b/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/forwards.js @@ -43,7 +43,7 @@ function fmt(fmt /*, ...*/) { function forward_proto_txt(s) { return fmt('%s-%s', - fwtool.fmt_family(uci.get('firewall', s, 'family')), + fwtool.fmt_family('ipv4'), fwtool.fmt_proto(uci.get('firewall', s, 'proto'), uci.get('firewall', s, 'icmp_type')) || 'TCP+UDP'); } @@ -79,14 +79,22 @@ return L.view.extend({ expect: { '': {} } }), + callConntrackHelpers: rpc.declare({ + object: 'luci', + method: 'getConntrackHelpers', + expect: { result: [] } + }), + load: function() { return Promise.all([ - this.callHostHints() + this.callHostHints(), + this.callConntrackHelpers() ]); }, render: function(data) { var hosts = data[0], + ctHelpers = data[1], m, s, o; m = new form.Map('firewall', _('Firewall - Port Forwards'), @@ -264,6 +272,37 @@ return L.view.extend({ o.rmempty = true; o.default = o.enabled; + o = s.taboption('advanced', form.ListValue, 'reflection_src', _('Loopback source IP'), _('Specifies whether to use the external or the internal IP address for reflected traffic.')); + o.modalonly = true; + o.depends('reflection', '1'); + o.value('internal', _('Use internal IP address')); + o.value('external', _('Use external IP address')); + o.write = function(section_id, value) { + uci.set('firewall', section_id, 'reflection_src', (value != 'internal') ? value : null); + }; + + o = s.taboption('advanced', form.Value, 'helper', _('Match helper'), _('Match traffic using the specified connection tracking helper.')); + o.modalonly = true; + o.placeholder = _('any'); + for (var i = 0; i < ctHelpers.length; i++) + o.value(ctHelpers[i].name, '%s (%s)'.format(ctHelpers[i].description, ctHelpers[i].name.toUpperCase())); + o.validate = function(section_id, value) { + if (value == '' || value == null) + return true; + + value = value.replace(/^!\s*/, ''); + + for (var i = 0; i < ctHelpers.length; i++) + if (value == ctHelpers[i].name) + return true; + + return _('Unknown or not installed conntrack helper "%s"').format(value); + }; + + fwtool.addMarkOption(s, false); + fwtool.addLimitOption(s); + fwtool.addLimitBurstOption(s); + o = s.taboption('advanced', form.Value, 'extra', _('Extra arguments'), _('Passes additional arguments to iptables. Use with care!')); o.modalonly = true; diff --git a/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/rules.js b/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/rules.js index a0d4cfc063..6c6efc805f 100644 --- a/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/rules.js +++ b/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/rules.js @@ -148,14 +148,23 @@ return L.view.extend({ expect: { '': {} } }), + callConntrackHelpers: rpc.declare({ + object: 'luci', + method: 'getConntrackHelpers', + expect: { result: [] } + }), + load: function() { - return this.callHostHints().catch(function(e) { - console.debug('load fail', e); - }); + return Promise.all([ + this.callHostHints(), + this.callConntrackHelpers() + ]); }, - render: function(hosts) { - var m, s, o; + render: function(data) { + var hosts = data[0], + ctHelpers = data[1], + m, s, o; m = new form.Map('firewall', _('Firewall - Traffic Rules'), _('Traffic rules define policies for packets traveling between different zones, for example to reject traffic between certain hosts or to open WAN ports on the router.')); @@ -223,9 +232,34 @@ return L.view.extend({ o.default = o.enabled; o.editable = true; - //ft.opt_enabled(s, Button); - //ft.opt_name(s, Value, _('Name')); + o = s.taboption('advanced', form.ListValue, 'direction', _('Match device')); + o.modalonly = true; + o.value('', _('unspecified')); + o.value('in', _('Inbound device')); + o.value('out', _('Outbound device')); + o.cfgvalue = function(section_id) { + var val = uci.get('firewall', section_id, 'direction'); + switch (val) { + case 'in': + case 'ingress': + return 'in'; + + case 'out': + case 'egress': + return 'out'; + } + + return null; + }; + + o = s.taboption('advanced', widgets.DeviceSelect, 'device', _('Device name'), + _('Specifies whether to tie this traffic rule to a specific inbound or outbound network device.')); + o.modalonly = true; + o.noaliases = true; + o.rmempty = false; + o.depends('direction', 'in'); + o.depends('direction', 'out'); o = s.taboption('advanced', form.ListValue, 'family', _('Restrict to address family')); o.modalonly = true; @@ -358,6 +392,56 @@ return L.view.extend({ o.value('ACCEPT', _('accept')); o.value('REJECT', _('reject')); o.value('NOTRACK', _("don't track")); + o.value('HELPER', _('assign conntrack helper')); + o.value('MARK_SET', _('apply firewall mark')); + o.value('MARK_XOR', _('XOR firewall mark')); + o.value('DSCP', _('DSCP classification')); + o.cfgvalue = function(section_id) { + var t = uci.get('firewall', section_id, 'target'), + m = uci.get('firewall', section_id, 'set_mark'); + + if (t == 'MARK') + return m ? 'MARK_SET' : 'MARK_XOR'; + + return t; + }; + o.write = function(section_id, value) { + return this.super('write', [section_id, (value == 'MARK_SET' || value == 'MARK_XOR') ? 'MARK' : value]); + }; + + fwtool.addMarkOption(s, 1); + fwtool.addMarkOption(s, 2); + fwtool.addDSCPOption(s, true); + + o = s.taboption('general', form.ListValue, 'set_helper', _('Tracking helper'), _('Assign the specified connection tracking helper to matched traffic.')); + o.modalonly = true; + o.placeholder = _('any'); + o.depends('target', 'HELPER'); + for (var i = 0; i < ctHelpers.length; i++) + o.value(ctHelpers[i].name, '%s (%s)'.format(ctHelpers[i].description, ctHelpers[i].name.toUpperCase())); + + o = s.taboption('advanced', form.Value, 'helper', _('Match helper'), _('Match traffic using the specified connection tracking helper.')); + o.modalonly = true; + o.placeholder = _('any'); + for (var i = 0; i < ctHelpers.length; i++) + o.value(ctHelpers[i].name, '%s (%s)'.format(ctHelpers[i].description, ctHelpers[i].name.toUpperCase())); + o.validate = function(section_id, value) { + if (value == '' || value == null) + return true; + + value = value.replace(/^!\s*/, ''); + + for (var i = 0; i < ctHelpers.length; i++) + if (value == ctHelpers[i].name) + return true; + + return _('Unknown or not installed conntrack helper "%s"').format(value); + }; + + fwtool.addMarkOption(s, false); + fwtool.addDSCPOption(s, false); + fwtool.addLimitOption(s); + fwtool.addLimitBurstOption(s); o = s.taboption('advanced', form.Value, 'extra', _('Extra arguments'), _('Passes additional arguments to iptables. Use with care!')); diff --git a/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/snats.js b/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/snats.js index 48fd98ff28..919a418fe6 100644 --- a/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/snats.js +++ b/applications/luci-app-firewall/htdocs/luci-static/resources/view/firewall/snats.js @@ -81,10 +81,11 @@ function snat_proto_txt(s) { var m = uci.get('firewall', s, 'mark'), p = uci.get('firewall', s, 'proto'); - return fmt(_('Match %{protocol?%{family} %{protocol} traffic:any %{family} traffic} %{mark?with firewall mark %{mark}}'), { + return fmt(_('Match %{protocol?%{family} %{protocol} traffic:any %{family} traffic} %{mark?with firewall mark %{mark}} %{limit?limited to %{limit}}'), { protocol: (p && p != 'all' && p != 'any' && p != '*') ? fwtool.fmt_proto(uci.get('firewall', s, 'proto')) : null, family: fwtool.fmt_family('ipv4'), - mark: m ? E('var', {}, fwtool.fmt_neg(m)) : null + mark: m ? E('var', {}, fwtool.fmt_neg(m)) : null, + limit: fwtool.fmt_limit(uci.get('firewall', s, 'limit'), uci.get('firewall', s, 'limit_burst')) }); } @@ -312,21 +313,9 @@ return L.view.extend({ o.modalonly = true; o.rmempty = true; - o = s.taboption('advanced', form.Value, 'mark', _('Match mark'), - _('Matches a specific firewall mark or a range of different marks.')); - o.modalonly = true; - o.rmempty = true; - o.validate = function(section_id, value) { - if (value == '') - return true; - - var m = String(value).match(/^(?:!\s*)?(0x[0-9a-f]{1,8}|[0-9]{1,10})(?:\/(0x[0-9a-f]{1,8}|[0-9]{1,10}))?$/i); - - if (!m || +m[1] > 0xffffffff || (m[2] != null && +m[2] > 0xffffffff)) - return _('Expecting: %s').format(_('valid firewall mark')); - - return true; - }; + fwtool.addMarkOption(s, false); + fwtool.addLimitOption(s); + fwtool.addLimitBurstOption(s); o = s.taboption('advanced', form.Value, 'extra', _('Extra arguments'), _('Passes additional arguments to iptables. Use with care!')); |