6 'require tools.firewall as fwtool';
7 'require tools.widgets as widgets';
9 function fmt(fmt /*, ...*/) {
10 var repl = [], wrap = false;
12 for (var i = 1; i < arguments.length; i++) {
13 if (L.dom.elem(arguments[i])) {
14 switch (arguments[i].nodeType) {
16 repl.push(arguments[i].outerHTML);
21 repl.push(arguments[i].data);
26 span.appendChild(arguments[i]);
27 repl.push(span.innerHTML);
36 repl.push(arguments[i]);
40 var rv = fmt.format.apply(fmt, repl);
41 return wrap ? E('span', rv) : rv;
44 function forward_proto_txt(s) {
46 fwtool.fmt_family('ipv4'),
47 fwtool.fmt_proto(uci.get('firewall', s, 'proto'),
48 uci.get('firewall', s, 'icmp_type')) || 'TCP+UDP');
51 function forward_src_txt(s) {
52 var z = fwtool.fmt_zone(uci.get('firewall', s, 'src'), _('any zone')),
53 a = fwtool.fmt_ip(uci.get('firewall', s, 'src_ip'), _('any host')),
54 p = fwtool.fmt_port(uci.get('firewall', s, 'src_port')),
55 m = fwtool.fmt_mac(uci.get('firewall', s, 'src_mac'));
58 return fmt(_('From %s in %s with source %s and %s'), a, z, p, m);
60 return fmt(_('From %s in %s with source %s'), a, z, p || m);
62 return fmt(_('From %s in %s'), a, z);
65 function forward_via_txt(s) {
66 var a = fwtool.fmt_ip(uci.get('firewall', s, 'src_dip'), _('any router IP')),
67 p = fwtool.fmt_port(uci.get('firewall', s, 'src_dport'));
70 return fmt(_('Via %s at %s'), a, p);
72 return fmt(_('Via %s'), a);
75 return L.view.extend({
76 callHostHints: rpc.declare({
78 method: 'getHostHints',
82 callConntrackHelpers: rpc.declare({
84 method: 'getConntrackHelpers',
85 expect: { result: [] }
91 this.callConntrackHelpers()
95 render: function(data) {
100 m = new form.Map('firewall', _('Firewall - Port Forwards'),
101 _('Port forwarding allows remote computers on the Internet to connect to a specific computer or service within the private LAN.'));
103 s = m.section(form.GridSection, 'redirect', _('Port Forwards'));
108 s.tab('general', _('General Settings'));
109 s.tab('advanced', _('Advanced Settings'));
111 s.filter = function(section_id) {
112 return (uci.get('firewall', section_id, 'target') != 'SNAT');
115 s.sectiontitle = function(section_id) {
116 return uci.get('firewall', section_id, 'name') || _('Unnamed forward');
119 s.handleAdd = function(ev) {
120 var config_name = this.uciconfig || this.map.config,
121 section_id = uci.add(config_name, this.sectiontype);
123 uci.set(config_name, section_id, 'target', 'DNAT');
125 this.addedSection = section_id;
126 this.renderMoreOptionsModal(section_id);
129 o = s.taboption('general', form.Value, 'name', _('Name'));
130 o.placeholder = _('Unnamed forward');
133 o = s.option(form.DummyValue, '_match', _('Match'));
135 o.textvalue = function(s) {
137 forward_proto_txt(s), E('br'),
138 forward_src_txt(s), E('br'),
143 o = s.option(form.ListValue, '_dest', _('Forward to'));
145 o.textvalue = function(s) {
146 var z = fwtool.fmt_zone(uci.get('firewall', s, 'dest'), _('any zone')),
147 a = fwtool.fmt_ip(uci.get('firewall', s, 'dest_ip'), _('any host')),
148 p = fwtool.fmt_port(uci.get('firewall', s, 'dest_port')) ||
149 fwtool.fmt_port(uci.get('firewall', s, 'src_dport'));
152 return fmt(_('%s, %s in %s'), a, p, z);
154 return fmt(_('%s in %s'), a, z);
157 o = s.option(form.Flag, 'enabled', _('Enable'));
159 o.default = o.enabled;
162 o = s.taboption('general', form.Value, 'proto', _('Protocol'));
164 o.default = 'tcp udp';
165 o.value('tcp udp', 'TCP+UDP');
166 o.value('tcp', 'TCP');
167 o.value('udp', 'UDP');
168 o.value('icmp', 'ICMP');
170 o.cfgvalue = function(/* ... */) {
171 var v = this.super('cfgvalue', arguments);
172 return (v == 'tcpudp') ? 'tcp udp' : v;
175 o = s.taboption('general', widgets.ZoneSelect, 'src', _('Source zone'));
181 o = s.taboption('advanced', form.Value, 'src_mac', _('Source MAC address'),
182 _('Only match incoming traffic from these MACs.'));
185 o.datatype = 'neg(macaddr)';
186 o.placeholder = E('em', _('any'));
187 L.sortedKeys(hosts).forEach(function(mac) {
188 o.value(mac, '%s (%s)'.format(
190 hosts[mac].name || hosts[mac].ipv4 || hosts[mac].ipv6 || '?'
194 o = s.taboption('advanced', form.Value, 'src_ip', _('Source IP address'),
195 _('Only match incoming traffic from this IP or range.'));
198 o.datatype = 'neg(ipmask4)';
199 o.placeholder = E('em', _('any'));
200 L.sortedKeys(hosts, 'ipv4', 'addr').forEach(function(mac) {
201 o.value(hosts[mac].ipv4, '%s (%s)'.format(
203 hosts[mac].name || mac
207 o = s.taboption('advanced', form.Value, 'src_port', _('Source port'),
208 _('Only match incoming traffic originating from the given source port or port range on the client host'));
211 o.datatype = 'neg(portrange)';
212 o.placeholder = _('any');
213 o.depends('proto', 'tcp');
214 o.depends('proto', 'udp');
215 o.depends('proto', 'tcp udp');
216 o.depends('proto', 'tcpudp');
218 o = s.taboption('advanced', form.Value, 'src_dip', _('External IP address'),
219 _('Only match incoming traffic directed at the given IP address.'));
222 o.datatype = 'neg(ipmask4)';
223 o.placeholder = E('em', _('any'));
224 L.sortedKeys(hosts, 'ipv4', 'addr').forEach(function(mac) {
225 o.value(hosts[mac].ipv4, '%s (%s)'.format(
227 hosts[mac].name || mac
231 o = s.taboption('general', form.Value, 'src_dport', _('External port'),
232 _('Match incoming traffic directed at the given destination port or port range on this host'));
235 o.datatype = 'neg(portrange)';
236 o.depends('proto', 'tcp');
237 o.depends('proto', 'udp');
238 o.depends('proto', 'tcp udp');
239 o.depends('proto', 'tcpudp');
241 o = s.taboption('general', widgets.ZoneSelect, 'dest', _('Internal zone'));
247 o = s.taboption('general', form.Value, 'dest_ip', _('Internal IP address'),
248 _('Redirect matched incoming traffic to the specified internal host'));
251 o.datatype = 'ipmask4';
252 L.sortedKeys(hosts, 'ipv4', 'addr').forEach(function(mac) {
253 o.value(hosts[mac].ipv4, '%s (%s)'.format(
255 hosts[mac].name || mac
259 o = s.taboption('general', form.Value, 'dest_port', _('Internal port'),
260 _('Redirect matched incoming traffic to the given port on the internal host'));
263 o.placeholder = _('any');
264 o.datatype = 'portrange';
265 o.depends('proto', 'tcp');
266 o.depends('proto', 'udp');
267 o.depends('proto', 'tcp udp');
268 o.depends('proto', 'tcpudp');
270 o = s.taboption('advanced', form.Flag, 'reflection', _('Enable NAT Loopback'));
273 o.default = o.enabled;
275 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.'));
277 o.depends('reflection', '1');
278 o.value('internal', _('Use internal IP address'));
279 o.value('external', _('Use external IP address'));
280 o.write = function(section_id, value) {
281 uci.set('firewall', section_id, 'reflection_src', (value != 'internal') ? value : null);
284 o = s.taboption('advanced', form.Value, 'helper', _('Match helper'), _('Match traffic using the specified connection tracking helper.'));
286 o.placeholder = _('any');
287 for (var i = 0; i < ctHelpers.length; i++)
288 o.value(ctHelpers[i].name, '%s (%s)'.format(ctHelpers[i].description, ctHelpers[i].name.toUpperCase()));
289 o.validate = function(section_id, value) {
290 if (value == '' || value == null)
293 value = value.replace(/^!\s*/, '');
295 for (var i = 0; i < ctHelpers.length; i++)
296 if (value == ctHelpers[i].name)
299 return _('Unknown or not installed conntrack helper "%s"').format(value);
302 o = s.taboption('advanced', form.Value, 'extra', _('Extra arguments'),
303 _('Passes additional arguments to iptables. Use with care!'));