6 'require tools.firewall as fwtool';
7 'require tools.widgets as widgets';
9 function fmt(fmtstr
, args
) {
10 var repl
= [], wrap
= false;
13 for (var i
= 0, last
= 0; i
<= fmtstr
.length
; i
++) {
14 if (fmtstr
.charAt(i
) == '%' && fmtstr
.charAt(i
+ 1) == '{') {
16 tokens
.push(fmtstr
.substring(last
, i
));
18 var j
= i
+ 1, nest
= 0;
22 for (var off
= j
+ 1, esc
= false; j
<= fmtstr
.length
; j
++) {
26 else if (fmtstr
.charAt(j
) == '\\') {
29 else if (fmtstr
.charAt(j
) == '{') {
32 else if (fmtstr
.charAt(j
) == '}') {
34 subexpr
.push(fmtstr
.substring(off
, j
));
38 else if (fmtstr
.charAt(j
) == '?' || fmtstr
.charAt(j
) == ':') {
40 subexpr
.push(fmtstr
.substring(off
, j
));
41 subexpr
.push(fmtstr
.charAt(j
));
47 var varname
= subexpr
[0].trim(),
48 op1
= (subexpr
[1] != null) ? subexpr
[1] : '?',
49 if_set
= (subexpr
[2] != null && subexpr
[2] != '') ? subexpr
[2] : '%{' + varname
+ '}',
50 op2
= (subexpr
[3] != null) ? subexpr
[3] : ':',
51 if_unset
= (subexpr
[4] != null) ? subexpr
[4] : '';
53 /* Invalid expression */
54 if (nest
!= 0 || subexpr
.length
> 5 || varname
== '' || op1
!= '?' || op2
!= ':')
57 if (subexpr
.length
== 1)
58 tokens
.push(args
[varname
] != null ? args
[varname
] : '');
59 else if (args
[varname
] != null)
60 tokens
.push(fmt(if_set
.replace(/\\(.)/g, '$1'), args
));
62 tokens
.push(fmt(if_unset
.replace(/\\(.)/g, '$1'), args
));
67 else if (i
>= fmtstr
.length
) {
69 tokens
.push(fmtstr
.substring(last
, i
));
73 for (var i
= 0; i
< tokens
.length
; i
++)
74 if (typeof(tokens
[i
]) == 'object')
75 return E('span', {}, tokens
);
77 return tokens
.join('');
80 function snat_proto_txt(s
) {
81 var m
= uci
.get('firewall', s
, 'mark'),
82 p
= uci
.get('firewall', s
, 'proto');
84 return fmt(_('Match %{protocol?%{family} %{protocol} traffic:any %{family} traffic} %{mark?with firewall mark %{mark}} %{limit?limited to %{limit}}'), {
85 protocol
: (p
&& p
!= 'all' && p
!= 'any' && p
!= '*') ? fwtool
.fmt_proto(uci
.get('firewall', s
, 'proto')) : null,
86 family
: fwtool
.fmt_family('ipv4'),
87 mark
: m
? E('var', {}, fwtool
.fmt_neg(m
)) : null,
88 limit
: fwtool
.fmt_limit(uci
.get('firewall', s
, 'limit'), uci
.get('firewall', s
, 'limit_burst'))
92 function snat_src_txt(s
) {
93 return fmt(_('From %{ipaddr?:any host} %{port?with source %{port}}'), {
94 ipaddr
: fwtool
.fmt_ip(uci
.get('firewall', s
, 'src_ip')),
95 port
: fwtool
.fmt_port(uci
.get('firewall', s
, 'src_port'))
99 function snat_dest_txt(s
) {
100 var z
= uci
.get('firewall', s
, 'src'),
101 d
= uci
.get('firewall', s
, 'device');
103 return fmt(_('To %{ipaddr?:any destination} %{port?at %{port}} %{zone?via zone %{zone}} %{device?egress device %{device}}'), {
104 port
: fwtool
.fmt_port(uci
.get('firewall', s
, 'dest_port')),
105 ipaddr
: fwtool
.fmt_ip(uci
.get('firewall', s
, 'dest_ip')),
106 zone
: (z
!= '*') ? fwtool
.fmt_zone(z
) : null,
107 device
: d
? E('var', {}, [d
]) : null
111 function snat_rewrite_txt(s
) {
112 var t
= uci
.get('firewall', s
, 'target'),
113 l
= fwtool
.fmt_limit(uci
.get('firewall', s
, 'limit'), uci
.get('firewall', s
, 'limit_burst'));
116 return fmt(_('Rewrite to %{ipaddr?%{port?%{ipaddr}, %{port}:%{ipaddr}}:%{port}}'), {
117 ipaddr
: fwtool
.fmt_ip(uci
.get('firewall', s
, 'snat_ip')),
118 port
: fwtool
.fmt_port(uci
.get('firewall', s
, 'snat_port'))
121 else if (t
== 'MASQUERADE') {
122 return _('Rewrite to outbound device IP');
124 else if (t
== 'ACCEPT') {
125 return _('Do not rewrite');
129 return L
.view
.extend({
130 callHostHints
: rpc
.declare({
132 method
: 'getHostHints',
136 callNetworkDevices
: rpc
.declare({
138 method
: 'getNetworkDevices',
144 this.callHostHints(),
145 this.callNetworkDevices()
149 render: function(data
) {
154 m
= new form
.Map('firewall', _('Firewall - NAT Rules'),
155 _('NAT rules allow fine grained control over the source IP to use for outbound or forwarded traffic.'));
157 s
= m
.section(form
.GridSection
, 'nat', _('NAT Rules'));
162 s
.tab('general', _('General Settings'));
163 s
.tab('advanced', _('Advanced Settings'));
164 s
.tab('timed', _('Time Restrictions'));
166 s
.sectiontitle = function(section_id
) {
167 return uci
.get('firewall', section_id
, 'name') || _('Unnamed NAT');
170 o
= s
.taboption('general', form
.Value
, 'name', _('Name'));
171 o
.placeholder
= _('Unnamed NAT');
174 o
= s
.option(form
.DummyValue
, '_match', _('Match'));
176 o
.textvalue = function(s
) {
178 snat_proto_txt(s
), E('br'),
179 snat_src_txt(s
), E('br'),
184 o
= s
.option(form
.ListValue
, '_dest', _('Rewrite to'));
186 o
.textvalue = function(s
) {
187 return snat_rewrite_txt(s
);
190 o
= s
.option(form
.Flag
, 'enabled', _('Enable'));
192 o
.default = o
.enabled
;
195 o
= s
.taboption('general', form
.Value
, 'proto', _('Protocol'));
198 o
.value('all', _('Any'));
199 o
.value('tcp udp', 'TCP+UDP');
200 o
.value('tcp', 'TCP');
201 o
.value('udp', 'UDP');
202 o
.cfgvalue = function(/* ... */) {
203 var v
= this.super('cfgvalue', arguments
);
204 return (v
== 'tcpudp') ? 'tcp udp' : v
;
207 o
= s
.taboption('general', widgets
.ZoneSelect
, 'src', _('Outbound zone'));
214 o
= s
.taboption('general', form
.Value
, 'src_ip', _('Source IP address'),
215 _('Match forwarded traffic from this IP or range.'));
218 o
.datatype
= 'neg(ipmask4)';
219 o
.placeholder
= E('em', _('any'));
220 L
.sortedKeys(hosts
, 'ipv4', 'addr').forEach(function(mac
) {
221 o
.value(hosts
[mac
].ipv4
, '%s (%s)'.format(
223 hosts
[mac
].name
|| mac
227 o
= s
.taboption('general', form
.Value
, 'src_port', _('Source port'),
228 _('Match forwarded traffic originating from the given source port or port range.'));
231 o
.datatype
= 'neg(portrange)';
232 o
.placeholder
= _('any');
233 o
.depends('proto', 'tcp');
234 o
.depends('proto', 'udp');
235 o
.depends('proto', 'tcp udp');
236 o
.depends('proto', 'tcpudp');
238 o
= s
.taboption('general', form
.Value
, 'dest_ip', _('Destination IP address'),
239 _('Match forwarded traffic directed at the given IP address.'));
242 o
.datatype
= 'neg(ipmask4)';
243 o
.placeholder
= E('em', _('any'));
244 L
.sortedKeys(hosts
, 'ipv4', 'addr').forEach(function(mac
) {
245 o
.value(hosts
[mac
].ipv4
, '%s (%s)'.format(
247 hosts
[mac
].name
|| mac
251 o
= s
.taboption('general', form
.Value
, 'dest_port', _('Destination port'),
252 _('Match forwarded traffic directed at the given destination port or port range.'));
255 o
.placeholder
= _('any');
256 o
.datatype
= 'neg(portrange)';
257 o
.depends('proto', 'tcp');
258 o
.depends('proto', 'udp');
259 o
.depends('proto', 'tcp udp');
260 o
.depends('proto', 'tcpudp');
262 o
= s
.taboption('general', form
.ListValue
, 'target', _('Action'));
265 o
.value('SNAT', _('SNAT - Rewrite to specific source IP or port'));
266 o
.value('MASQUERADE', _('MASQUERADE - Automatically rewrite to outbound interface IP'));
267 o
.value('ACCEPT', _('ACCEPT - Disable address rewriting'));
269 o
= s
.taboption('general', form
.Value
, 'snat_ip', _('Rewrite IP address'),
270 _('Rewrite matched traffic to the specified source IP address.'));
273 o
.placeholder
= _('do not rewrite');
274 o
.datatype
= 'ip4addr("nomask")';
275 o
.validate = function(section_id
, value
) {
276 var port
= this.map
.lookupOption('snat_port', section_id
),
277 p
= port
? port
[0].formvalue(section_id
) : null;
279 if ((value
== null || value
== '') && (p
== null || p
== ''))
280 return _('A rewrite IP must be specified!');
284 o
.depends('target', 'SNAT');
285 L
.sortedKeys(devs
, 'name').forEach(function(dev
) {
286 var ip4addrs
= devs
[dev
].ipaddrs
;
288 if (!L
.isObject(devs
[dev
].flags
) || !Array
.isArray(ip4addrs
) || devs
[dev
].flags
.loopback
)
291 for (var i
= 0; i
< ip4addrs
.length
; i
++) {
292 if (!L
.isObject(ip4addrs
[i
]) || !ip4addrs
[i
].address
)
295 o
.value(ip4addrs
[i
].address
, '%s (%s)'.format(ip4addrs
[i
].address
, dev
));
299 o
= s
.taboption('general', form
.Value
, 'snat_port', _('Rewrite port'),
300 _('Rewrite matched traffic to the specified source port or port range.'));
303 o
.placeholder
= _('do not rewrite');
304 o
.datatype
= 'portrange';
305 o
.depends({ target
: 'SNAT', proto
: 'tcp' });
306 o
.depends({ target
: 'SNAT', proto
: 'udp' });
307 o
.depends({ target
: 'SNAT', proto
: 'tcp udp' });
308 o
.depends({ target
: 'SNAT', proto
: 'tcpudp' });
310 o
= s
.taboption('advanced', widgets
.DeviceSelect
, 'device', _('Outbound device'),
311 _('Matches forwarded traffic using the specified outbound network device.'));
316 fwtool
.addMarkOption(s
, false);
317 fwtool
.addLimitOption(s
);
318 fwtool
.addLimitBurstOption(s
);
320 o
= s
.taboption('advanced', form
.Value
, 'extra', _('Extra arguments'),
321 _('Passes additional arguments to iptables. Use with care!'));
325 o
= s
.taboption('timed', form
.MultiValue
, 'weekdays', _('Week Days'));
329 o
.placeholder
= _('Any day');
330 o
.value('Sun', _('Sunday'));
331 o
.value('Mon', _('Monday'));
332 o
.value('Tue', _('Tuesday'));
333 o
.value('Wed', _('Wednesday'));
334 o
.value('Thu', _('Thursday'));
335 o
.value('Fri', _('Friday'));
336 o
.value('Sat', _('Saturday'));
337 o
.write = function(section_id
, value
) {
338 return this.super('write', [ section_id
, L
.toArray(value
).join(' ') ]);
341 o
= s
.taboption('timed', form
.MultiValue
, 'monthdays', _('Month Days'));
345 o
.placeholder
= _('Any day');
346 o
.write = function(section_id
, value
) {
347 return this.super('write', [ section_id
, L
.toArray(value
).join(' ') ]);
349 for (var i
= 1; i
<= 31; i
++)
352 o
= s
.taboption('timed', form
.Value
, 'start_time', _('Start Time (hh.mm.ss)'));
354 o
.datatype
= 'timehhmmss';
356 o
= s
.taboption('timed', form
.Value
, 'stop_time', _('Stop Time (hh.mm.ss)'));
358 o
.datatype
= 'timehhmmss';
360 o
= s
.taboption('timed', form
.Value
, 'start_date', _('Start Date (yyyy-mm-dd)'));
362 o
.datatype
= 'dateyyyymmdd';
364 o
= s
.taboption('timed', form
.Value
, 'stop_date', _('Stop Date (yyyy-mm-dd)'));
366 o
.datatype
= 'dateyyyymmdd';
368 o
= s
.taboption('timed', form
.Flag
, 'utc_time', _('Time in UTC'));
370 o
.default = o
.disabled
;