4 'require tools.prng as random';
7 function initFirewallState() {
8 return L
.resolveDefault(uci
.load('firewall'));
11 function parseEnum(s
, values
) {
15 s
= String(s
).toUpperCase();
20 for (var i
= 0; i
< values
.length
; i
++)
21 if (values
[i
].toUpperCase().indexOf(s
) == 0)
27 function parsePolicy(s
, defaultValue
) {
28 return parseEnum(s
, ['DROP', 'REJECT', 'ACCEPT']) || (arguments
.length
< 2 ? null : defaultValue
);
32 var Firewall
, AbstractFirewallItem
, Defaults
, Zone
, Forwarding
, Redirect
, Rule
;
34 function lookupZone(name
) {
35 var z
= uci
.get('firewall', name
);
37 if (z
!= null && z
['.type'] == 'zone')
38 return new Zone(z
['.name']);
40 var sections
= uci
.sections('firewall', 'zone');
42 for (var i
= 0; i
< sections
.length
; i
++) {
43 if (sections
[i
].name
!= name
)
46 return new Zone(sections
[i
]['.name']);
52 function getColorForName(forName
) {
55 else if (forName
== 'lan')
57 else if (forName
== 'wan')
60 return random
.derive_color(forName
);
64 Firewall
= L
.Class
.extend({
65 getDefaults: function() {
66 return initFirewallState().then(function() {
67 return new Defaults();
72 return initFirewallState().then(L
.bind(function() {
76 while (this.getZone(name
) != null)
77 name
= 'newzone%d'.format(++count
);
79 return this.addZone(name
);
83 addZone: function(name
) {
84 return initFirewallState().then(L
.bind(function() {
85 if (name
== null || !/^[a-zA-Z0-9_]+$/.test(name
))
88 if (lookupZone(name
) != null)
91 var d
= new Defaults(),
92 z
= uci
.add('firewall', 'zone');
94 uci
.set('firewall', z
, 'name', name
);
95 uci
.set('firewall', z
, 'input', d
.getInput() || 'DROP');
96 uci
.set('firewall', z
, 'output', d
.getOutput() || 'DROP');
97 uci
.set('firewall', z
, 'forward', d
.getForward() || 'DROP');
103 getZone: function(name
) {
104 return initFirewallState().then(function() {
105 return lookupZone(name
);
109 getZones: function() {
110 return initFirewallState().then(function() {
111 var sections
= uci
.sections('firewall', 'zone'),
114 for (var i
= 0; i
< sections
.length
; i
++)
115 zones
.push(new Zone(sections
[i
]['.name']));
117 zones
.sort(function(a
, b
) { return a
.getName() > b
.getName() });
123 getZoneByNetwork: function(network
) {
124 return initFirewallState().then(function() {
125 var sections
= uci
.sections('firewall', 'zone');
127 for (var i
= 0; i
< sections
.length
; i
++)
128 if (L
.toArray(sections
[i
].network
).indexOf(network
) != -1)
129 return new Zone(sections
[i
]['.name']);
135 deleteZone: function(name
) {
136 return initFirewallState().then(function() {
137 var section
= uci
.get('firewall', name
),
140 if (section
!= null && section
['.type'] == 'zone') {
143 uci
.remove('firewall', section
['.name']);
145 else if (name
!= null) {
146 var sections
= uci
.sections('firewall', 'zone');
148 for (var i
= 0; i
< sections
.length
; i
++) {
149 if (sections
[i
].name
!= name
)
153 uci
.remove('firewall', sections
[i
]['.name']);
158 sections
= uci
.sections('firewall');
160 for (var i
= 0; i
< sections
.length
; i
++) {
161 if (sections
[i
]['.type'] != 'rule' &&
162 sections
[i
]['.type'] != 'redirect' &&
163 sections
[i
]['.type'] != 'forwarding')
166 if (sections
[i
].src
== name
|| sections
[i
].dest
== name
)
167 uci
.remove('firewall', sections
[i
]['.name']);
175 renameZone: function(oldName
, newName
) {
176 return initFirewallState().then(L
.bind(function() {
177 if (oldName
== null || newName
== null || !/^[a-zA-Z0-9_]+$/.test(newName
))
180 if (lookupZone(newName
) != null)
183 var sections
= uci
.sections('firewall', 'zone'),
186 for (var i
= 0; i
< sections
.length
; i
++) {
187 if (sections
[i
].name
!= oldName
)
190 uci
.set('firewall', sections
[i
]['.name'], 'name', newName
);
195 sections
= uci
.sections('firewall');
197 for (var i
= 0; i
< sections
.length
; i
++) {
198 if (sections
[i
]['.type'] != 'rule' &&
199 sections
[i
]['.type'] != 'redirect' &&
200 sections
[i
]['.type'] != 'forwarding')
203 if (sections
[i
].src
== oldName
)
204 uci
.set('firewall', sections
[i
]['.name'], 'src', newName
);
206 if (sections
[i
].dest
== oldName
)
207 uci
.set('firewall', sections
[i
]['.name'], 'dest', newName
);
215 deleteNetwork: function(network
) {
216 return this.getZones().then(L
.bind(function(zones
) {
219 for (var i
= 0; i
< zones
.length
; i
++)
220 if (zones
[i
].deleteNetwork(network
))
227 getColorForName
: getColorForName
,
229 getZoneColorStyle: function(zone
) {
230 var hex
= (zone
instanceof Zone
) ? zone
.getColor() : getColorForName((zone
!= null && zone
!= '*') ? zone
: null);
232 return '--zone-color-rgb:%d, %d, %d; background-color:rgb(var(--zone-color-rgb))'.format(
233 parseInt(hex
.substring(1, 3), 16),
234 parseInt(hex
.substring(3, 5), 16),
235 parseInt(hex
.substring(5, 7), 16)
241 AbstractFirewallItem
= L
.Class
.extend({
242 get: function(option
) {
243 return uci
.get('firewall', this.sid
, option
);
246 set: function(option
, value
) {
247 return uci
.set('firewall', this.sid
, option
, value
);
252 Defaults
= AbstractFirewallItem
.extend({
253 __init__: function() {
254 var sections
= uci
.sections('firewall', 'defaults');
256 for (var i
= 0; i
< sections
.length
; i
++) {
257 this.sid
= sections
[i
]['.name'];
261 if (this.sid
== null)
262 this.sid
= uci
.add('firewall', 'defaults');
265 isSynFlood: function() {
266 return (this.get('syn_flood') == '1');
269 isDropInvalid: function() {
270 return (this.get('drop_invalid') == '1');
273 getInput: function() {
274 return parsePolicy(this.get('input'), 'DROP');
277 getOutput: function() {
278 return parsePolicy(this.get('output'), 'DROP');
281 getForward: function() {
282 return parsePolicy(this.get('forward'), 'DROP');
287 Zone
= AbstractFirewallItem
.extend({
288 __init__: function(name
) {
289 var section
= uci
.get('firewall', name
);
291 if (section
!= null && section
['.type'] == 'zone') {
295 else if (name
!= null) {
296 var sections
= uci
.get('firewall', 'zone');
298 for (var i
= 0; i
< sections
.length
; i
++) {
299 if (sections
[i
].name
!= name
)
302 this.sid
= sections
[i
]['.name'];
303 this.data
= sections
[i
];
309 isMasquerade: function() {
310 return (this.get('masq') == '1');
313 getName: function() {
314 return this.get('name');
317 getNetwork: function() {
318 return this.get('network');
321 getInput: function() {
322 return parsePolicy(this.get('input'), (new Defaults()).getInput());
325 getOutput: function() {
326 return parsePolicy(this.get('output'), (new Defaults()).getOutput());
329 getForward: function() {
330 return parsePolicy(this.get('forward'), (new Defaults()).getForward());
333 addNetwork: function(network
) {
334 var section
= uci
.get('network', network
);
336 if (section
== null || section
['.type'] != 'interface')
339 var newNetworks
= this.getNetworks();
341 if (newNetworks
.filter(function(net
) { return net
== network
}).length
)
344 newNetworks
.push(network
);
345 this.set('network', newNetworks
);
350 deleteNetwork: function(network
) {
351 var oldNetworks
= this.getNetworks(),
352 newNetworks
= oldNetworks
.filter(function(net
) { return net
!= network
});
354 if (newNetworks
.length
> 0)
355 this.set('network', newNetworks
);
357 this.set('network', null);
359 return (newNetworks
.length
< oldNetworks
.length
);
362 getNetworks: function() {
363 return L
.toArray(this.get('network'));
366 clearNetworks: function() {
367 this.set('network', null);
370 getDevices: function() {
371 return L
.toArray(this.get('device'));
374 getSubnets: function() {
375 return L
.toArray(this.get('subnet'));
378 getForwardingsBy: function(what
) {
379 var sections
= uci
.sections('firewall', 'forwarding'),
382 for (var i
= 0; i
< sections
.length
; i
++) {
383 if (sections
[i
].src
== null || sections
[i
].dest
== null)
386 if (sections
[i
][what
] != this.getName())
389 forwards
.push(new Forwarding(sections
[i
]['.name']));
395 addForwardingTo: function(dest
) {
396 var forwards
= this.getForwardingsBy('src'),
397 zone
= lookupZone(dest
);
399 if (zone
== null || zone
.getName() == this.getName())
402 for (var i
= 0; i
< forwards
.length
; i
++)
403 if (forwards
[i
].getDestination() == zone
.getName())
406 var sid
= uci
.add('firewall', 'forwarding');
408 uci
.set('firewall', sid
, 'src', this.getName());
409 uci
.set('firewall', sid
, 'dest', zone
.getName());
411 return new Forwarding(sid
);
414 addForwardingFrom: function(src
) {
415 var forwards
= this.getForwardingsBy('dest'),
416 zone
= lookupZone(src
);
418 if (zone
== null || zone
.getName() == this.getName())
421 for (var i
= 0; i
< forwards
.length
; i
++)
422 if (forwards
[i
].getSource() == zone
.getName())
425 var sid
= uci
.add('firewall', 'forwarding');
427 uci
.set('firewall', sid
, 'src', zone
.getName());
428 uci
.set('firewall', sid
, 'dest', this.getName());
430 return new Forwarding(sid
);
433 deleteForwardingsBy: function(what
) {
434 var sections
= uci
.sections('firewall', 'forwarding'),
437 for (var i
= 0; i
< sections
.length
; i
++) {
438 if (sections
[i
].src
== null || sections
[i
].dest
== null)
441 if (sections
[i
][what
] != this.getName())
444 uci
.remove('firewall', sections
[i
]['.name']);
451 deleteForwarding: function(forwarding
) {
452 if (!(forwarding
instanceof Forwarding
))
455 var section
= uci
.get('firewall', forwarding
.sid
);
457 if (!section
|| section
['.type'] != 'forwarding')
460 uci
.remove('firewall', section
['.name']);
465 addRedirect: function(options
) {
466 var sid
= uci
.add('firewall', 'redirect');
468 if (options
!= null && typeof(options
) == 'object')
469 for (var key
in options
)
470 if (options
.hasOwnProperty(key
))
471 uci
.set('firewall', sid
, key
, options
[key
]);
473 uci
.set('firewall', sid
, 'src', this.getName());
475 return new Redirect(sid
);
478 addRule: function(options
) {
479 var sid
= uci
.add('firewall', 'rule');
481 if (options
!= null && typeof(options
) == 'object')
482 for (var key
in options
)
483 if (options
.hasOwnProperty(key
))
484 uci
.set('firewall', sid
, key
, options
[key
]);
486 uci
.set('firewall', sid
, 'src', this.getName());
488 return new Rule(sid
);
491 getColor: function(forName
) {
492 var name
= (arguments
.length
> 0 ? forName
: this.getName());
494 return getColorForName(name
);
499 Forwarding
= AbstractFirewallItem
.extend({
500 __init__: function(sid
) {
504 getSource: function() {
505 return this.get('src');
508 getDestination: function() {
509 return this.get('dest');
512 getSourceZone: function() {
513 return lookupZone(this.getSource());
516 getDestinationZone: function() {
517 return lookupZone(this.getDestination());
522 Rule
= AbstractFirewallItem
.extend({
523 getSource: function() {
524 return this.get('src');
527 getDestination: function() {
528 return this.get('dest');
531 getSourceZone: function() {
532 return lookupZone(this.getSource());
535 getDestinationZone: function() {
536 return lookupZone(this.getDestination());
541 Redirect
= AbstractFirewallItem
.extend({
542 getSource: function() {
543 return this.get('src');
546 getDestination: function() {
547 return this.get('dest');
550 getSourceZone: function() {
551 return lookupZone(this.getSource());
554 getDestinationZone: function() {
555 return lookupZone(this.getDestination());