8 'require tools.firewall as fwtool';
9 'require tools.widgets as widgets';
12 callConntrackHelpers: rpc.declare({
14 method: 'getConntrackHelpers',
15 expect: { result: [] }
20 this.callConntrackHelpers(),
21 firewall.getDefaults()
25 render: function(data) {
26 if (fwtool.checkLegacySNAT())
27 return fwtool.renderMigration();
29 return this.renderZones(data);
32 renderZones: function(data) {
33 var ctHelpers = data[0],
37 m = new form.Map('firewall', _('Firewall - Zone Settings'),
38 _('The firewall creates zones over your network interfaces to control network traffic flow.'));
40 s = m.section(form.TypedSection, 'defaults', _('General Settings'));
44 o = s.option(form.Flag, 'syn_flood', _('Enable SYN-flood protection'));
45 o = s.option(form.Flag, 'drop_invalid', _('Drop invalid packets'));
48 s.option(form.ListValue, 'input', _('Input')),
49 s.option(form.ListValue, 'output', _('Output')),
50 s.option(form.ListValue, 'forward', _('Forward'))
53 for (var i = 0; i < p.length; i++) {
54 p[i].value('REJECT', _('reject'));
55 p[i].value('DROP', _('drop'));
56 p[i].value('ACCEPT', _('accept'));
59 /* Netfilter flow offload support */
61 if (L.hasSystemFeature('offloading')) {
62 s = m.section(form.TypedSection, 'defaults', _('Routing/NAT Offloading'),
63 _('Experimental feature. Not fully compatible with QoS/SQM.'));
68 o = s.option(form.Flag, 'flow_offloading',
69 _('Software flow offloading'),
70 _('Software based offloading for routing/NAT'));
73 o = s.option(form.Flag, 'flow_offloading_hw',
74 _('Hardware flow offloading'),
75 _('Requires hardware NAT support. Implemented at least for mt7621'));
77 o.depends('flow_offloading', '1');
81 s = m.section(form.GridSection, 'zone', _('Zones'));
86 s.handleRemove = function(section_id, ev) {
87 return firewall.deleteZone(section_id).then(L.bind(function() {
88 return this.super('handleRemove', [section_id, ev]);
92 s.tab('general', _('General Settings'));
93 s.tab('advanced', _('Advanced Settings'));
94 s.tab('conntrack', _('Conntrack Settings'));
95 s.tab('extra', _('Extra iptables arguments'));
97 o = s.taboption('general', form.DummyValue, '_generalinfo');
100 o.cfgvalue = function(section_id) {
101 var name = uci.get('firewall', section_id, 'name');
103 name = _("this new zone");
104 return _('This section defines common properties of %q. The <em>input</em> and <em>output</em> options set the default policies for traffic entering and leaving this zone while the <em>forward</em> option describes the policy for forwarded traffic between different networks within the zone. <em>Covered networks</em> specifies which available networks are members of this zone.')
105 .replace(/%s/g, name).replace(/%q/g, '"' + name + '"');
108 o = s.taboption('general', form.Value, 'name', _('Name'));
109 o.placeholder = _('Unnamed zone');
112 o.datatype = 'and(uciname,maxlength(11))';
113 o.write = function(section_id, formvalue) {
114 var cfgvalue = this.cfgvalue(section_id);
116 if (cfgvalue == null || cfgvalue == '')
117 return uci.set('firewall', section_id, 'name', formvalue);
118 else if (cfgvalue != formvalue)
119 return firewall.renameZone(cfgvalue, formvalue);
122 o = s.option(widgets.ZoneForwards, '_info', _('Zone ⇒ Forwardings'));
125 o.cfgvalue = function(section_id) {
126 return uci.get('firewall', section_id, 'name');
130 s.taboption('general', form.ListValue, 'input', _('Input')),
131 s.taboption('general', form.ListValue, 'output', _('Output')),
132 s.taboption('general', form.ListValue, 'forward', _('Forward'))
135 for (var i = 0; i < p.length; i++) {
136 p[i].value('REJECT', _('reject'));
137 p[i].value('DROP', _('drop'));
138 p[i].value('ACCEPT', _('accept'));
139 p[i].editable = true;
142 p[0].default = fwDefaults.getInput();
143 p[1].default = fwDefaults.getOutput();
144 p[2].default = fwDefaults.getForward();
146 o = s.taboption('general', form.Flag, 'masq', _('Masquerading'));
149 o = s.taboption('general', form.Flag, 'mtu_fix', _('MSS clamping'));
152 o = s.taboption('general', widgets.NetworkSelect, 'network', _('Covered networks'));
155 o.cfgvalue = function(section_id) {
156 return uci.get('firewall', section_id, 'network');
158 o.write = function(section_id, formvalue) {
159 var name = uci.get('firewall', section_id, 'name'),
160 cfgvalue = this.cfgvalue(section_id);
162 if (typeof(cfgvalue) == 'string' && Array.isArray(formvalue) && (cfgvalue == formvalue.join(' ')))
165 var tasks = [ firewall.getZone(name) ];
167 if (Array.isArray(formvalue))
168 for (var i = 0; i < formvalue.length; i++) {
169 var netname = formvalue[i];
170 tasks.push(network.getNetwork(netname).then(function(net) {
171 return net || network.addNetwork(netname, { 'proto': 'none' });
175 return Promise.all(tasks).then(function(zone_networks) {
176 if (zone_networks[0])
177 for (var i = 1; i < zone_networks.length; i++)
178 zone_networks[0].addNetwork(zone_networks[i].getName());
182 o = s.taboption('advanced', form.DummyValue, '_advancedinfo');
185 o.cfgvalue = function(section_id) {
186 var name = uci.get('firewall', section_id, 'name');
188 name = _("this new zone");
189 return _('The options below control the forwarding policies between this zone (%s) and other zones. <em>Destination zones</em> cover forwarded traffic <strong>originating from %q</strong>. <em>Source zones</em> match forwarded traffic from other zones <strong>targeted at %q</strong>. The forwarding rule is <em>unidirectional</em>, e.g. a forward from lan to wan does <em>not</em> imply a permission to forward from wan to lan as well.')
193 o = s.taboption('advanced', widgets.DeviceSelect, 'device', _('Covered devices'), _('Use this option to classify zone traffic by raw, non-<em>uci</em> managed network devices.'));
198 o = s.taboption('advanced', form.DynamicList, 'subnet', _('Covered subnets'), _('Use this option to classify zone traffic by source or destination subnet instead of networks or devices.'));
199 o.datatype = 'neg(cidr)';
203 o = s.taboption('advanced', form.ListValue, 'family', _('Restrict to address family'));
204 o.value('', _('IPv4 and IPv6'));
205 o.value('ipv4', _('IPv4 only'));
206 o.value('ipv6', _('IPv6 only'));
209 o = s.taboption('advanced', form.DynamicList, 'masq_src', _('Restrict Masquerading to given source subnets'));
210 o.depends('family', '');
211 o.depends('family', 'ipv4');
212 o.datatype = 'list(neg(or(uciname,hostname,ipmask4)))';
213 o.placeholder = '0.0.0.0/0';
216 o = s.taboption('advanced', form.DynamicList, 'masq_dest', _('Restrict Masquerading to given destination subnets'));
217 o.depends('family', '');
218 o.depends('family', 'ipv4');
219 o.datatype = 'list(neg(or(uciname,hostname,ipmask4)))';
220 o.placeholder = '0.0.0.0/0';
223 o = s.taboption('conntrack', form.Flag, 'masq_allow_invalid', _('Allow "invalid" traffic'), _('Do not install extra rules to reject forwarded traffic with conntrack state <em>invalid</em>. This may be required for complex asymmetric route setups.'));
226 o = s.taboption('conntrack', form.Flag, 'auto_helper', _('Automatic helper assignment'), _('Automatically assign conntrack helpers based on traffic protocol and port'));
227 o.default = o.enabled;
230 o = s.taboption('conntrack', form.MultiValue, 'helper', _('Conntrack helpers'), _('Explicitly choses allowed connection tracking helpers for zone traffic'));
231 o.depends('auto_helper', '0');
233 for (var i = 0; i < ctHelpers.length; i++)
234 o.value(ctHelpers[i].name, '<span class="hide-close">%s (%s)</span><span class="hide-open">%s</span>'.format(ctHelpers[i].description, ctHelpers[i].name.toUpperCase(), ctHelpers[i].name.toUpperCase()));
236 o = s.taboption('advanced', form.Flag, 'log', _('Enable logging on this zone'));
239 o = s.taboption('advanced', form.Value, 'log_limit', _('Limit log messages'));
240 o.depends('log', '1');
241 o.placeholder = '10/minute';
244 o = s.taboption('extra', form.DummyValue, '_extrainfo');
247 o.cfgvalue = function(section_id) {
248 return _('Passing raw iptables arguments to source and destination traffic classification rules allows to match packets based on other criteria than interfaces or subnets. These options should be used with extreme care as invalid values could render the firewall ruleset broken, completely exposing all services.');
251 o = s.taboption('extra', form.Value, 'extra_src', _('Extra source arguments'), _('Additional raw <em>iptables</em> arguments to classify zone source traffic, e.g. <code>-p tcp --sport 443</code> to only match inbound HTTPS traffic.'));
253 o.cfgvalue = function(section_id) {
254 return uci.get('firewall', section_id, 'extra_src') || uci.get('firewall', section_id, 'extra');
256 o.write = function(section_id, value) {
257 uci.unset('firewall', section_id, 'extra');
258 uci.set('firewall', section_id, 'extra_src', value);
261 o = s.taboption('extra', form.Value, 'extra_dest', _('Extra destination arguments'), _('Additional raw <em>iptables</em> arguments to classify zone destination traffic, e.g. <code>-p tcp --dport 443</code> to only match outbound HTTPS traffic.'));
263 o.cfgvalue = function(section_id) {
264 return uci.get('firewall', section_id, 'extra_dest') || uci.get('firewall', section_id, 'extra_src') || uci.get('firewall', section_id, 'extra');
266 o.write = function(section_id, value) {
267 uci.unset('firewall', section_id, 'extra');
268 uci.set('firewall', section_id, 'extra_dest', value);
271 o = s.taboption('general', form.DummyValue, '_forwardinfo');
274 o.cfgvalue = function(section_id) {
275 var name = uci.get('firewall', section_id, 'name');
277 name = _("this new zone");
278 return _('The options below control the forwarding policies between this zone (%s) and other zones. <em>Destination zones</em> cover forwarded traffic <strong>originating from %q</strong>. <em>Source zones</em> match forwarded traffic from other zones <strong>targeted at %q</strong>. The forwarding rule is <em>unidirectional</em>, e.g. a forward from lan to wan does <em>not</em> imply a permission to forward from wan to lan as well.')
282 out = o = s.taboption('general', widgets.ZoneSelect, 'out', _('Allow forward to <em>destination zones</em>:'));
286 o.filter = function(section_id, value) {
287 return (uci.get('firewall', section_id, 'name') != value);
289 o.cfgvalue = function(section_id) {
290 var out = (this.option == 'out'),
291 zone = this.lookupZone(uci.get('firewall', section_id, 'name')),
292 fwds = zone ? zone.getForwardingsBy(out ? 'src' : 'dest') : [],
295 for (var i = 0; i < fwds.length; i++)
296 value.push(out ? fwds[i].getDestination() : fwds[i].getSource());
300 o.write = o.remove = function(section_id, formvalue) {
301 var out = (this.option == 'out'),
302 zone = this.lookupZone(uci.get('firewall', section_id, 'name')),
303 fwds = zone ? zone.getForwardingsBy(out ? 'src' : 'dest') : [];
305 if (formvalue == null)
308 if (Array.isArray(formvalue)) {
309 for (var i = 0; i < fwds.length; i++) {
310 var cmp = out ? fwds[i].getDestination() : fwds[i].getSource();
311 if (!formvalue.filter(function(d) { return d == cmp }).length)
312 zone.deleteForwarding(fwds[i]);
315 for (var i = 0; i < formvalue.length; i++)
317 zone.addForwardingTo(formvalue[i]);
319 zone.addForwardingFrom(formvalue[i]);
323 inp = o = s.taboption('general', widgets.ZoneSelect, 'in', _('Allow forward from <em>source zones</em>:'));
327 o.write = o.remove = out.write;
328 o.filter = out.filter;
329 o.cfgvalue = out.cfgvalue;