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 random
.seed(parseInt(sfh(forName
), 16));
62 var r
= random
.get(128),
72 var b
= min
+ Math
.floor(random
.get() * (max
- min
));
74 return '#%02x%02x%02x'.format(0xff - r
, 0xff - g
, 0xff - b
);
78 Firewall
= L
.Class
.extend({
79 getDefaults: function() {
80 return initFirewallState().then(function() {
81 return new Defaults();
86 return initFirewallState().then(L
.bind(function() {
90 while (this.getZone(name
) != null)
91 name
= 'newzone%d'.format(++count
);
93 return this.addZone(name
);
97 addZone: function(name
) {
98 return initFirewallState().then(L
.bind(function() {
99 if (name
== null || !/^[a-zA-Z0-9_]+$/.test(name
))
102 if (lookupZone(name
) != null)
105 var d
= new Defaults(),
106 z
= uci
.add('firewall', 'zone');
108 uci
.set('firewall', z
, 'name', name
);
109 uci
.set('firewall', z
, 'network', ' ');
110 uci
.set('firewall', z
, 'input', d
.getInput() || 'DROP');
111 uci
.set('firewall', z
, 'output', d
.getOutput() || 'DROP');
112 uci
.set('firewall', z
, 'forward', d
.getForward() || 'DROP');
118 getZone: function(name
) {
119 return initFirewallState().then(function() {
120 return lookupZone(name
);
124 getZones: function() {
125 return initFirewallState().then(function() {
126 var sections
= uci
.sections('firewall', 'zone'),
129 for (var i
= 0; i
< sections
.length
; i
++)
130 zones
.push(new Zone(sections
[i
]['.name']));
132 zones
.sort(function(a
, b
) { return a
.getName() > b
.getName() });
138 getZoneByNetwork: function(network
) {
139 return initFirewallState().then(function() {
140 var sections
= uci
.sections('firewall', 'zone');
142 for (var i
= 0; i
< sections
.length
; i
++)
143 if (L
.toArray(sections
[i
].network
|| sections
[i
].name
).indexOf(network
) != -1)
144 return new Zone(sections
[i
]['.name']);
150 deleteZone: function(name
) {
151 return initFirewallState().then(function() {
152 var section
= uci
.get('firewall', name
),
155 if (section
!= null && section
['.type'] == 'zone') {
158 uci
.remove('firewall', section
['.name']);
160 else if (name
!= null) {
161 var sections
= uci
.sections('firewall', 'zone');
163 for (var i
= 0; i
< sections
.length
; i
++) {
164 if (sections
[i
].name
!= name
)
168 uci
.remove('firewall', sections
[i
]['.name']);
173 sections
= uci
.sections('firewall');
175 for (var i
= 0; i
< sections
.length
; i
++) {
176 if (sections
[i
]['.type'] != 'rule' &&
177 sections
[i
]['.type'] != 'redirect' &&
178 sections
[i
]['.type'] != 'forwarding')
181 if (sections
[i
].src
== name
|| sections
[i
].dest
== name
)
182 uci
.remove('firewall', sections
[i
]['.name']);
190 renameZone: function(oldName
, newName
) {
191 return initFirewallState().then(L
.bind(function() {
192 if (oldName
== null || newName
== null || !/^[a-zA-Z0-9_]+$/.test(newName
))
195 if (lookupZone(newName
) != null)
198 var sections
= uci
.sections('firewall', 'zone'),
201 for (var i
= 0; i
< sections
.length
; i
++) {
202 if (sections
[i
].name
!= oldName
)
205 if (L
.toArray(sections
[i
].network
).length
== 0)
206 uci
.set('firewall', sections
[i
]['.name'], 'network', oldName
);
208 uci
.set('firewall', sections
[i
]['.name'], 'name', newName
);
213 sections
= uci
.sections('firewall');
215 for (var i
= 0; i
< sections
.length
; i
++) {
216 if (sections
[i
]['.type'] != 'rule' &&
217 sections
[i
]['.type'] != 'redirect' &&
218 sections
[i
]['.type'] != 'forwarding')
221 if (sections
[i
].src
== oldName
)
222 uci
.set('firewall', sections
[i
]['.name'], 'src', newName
);
224 if (sections
[i
].dest
== oldName
)
225 uci
.set('firewall', sections
[i
]['.name'], 'dest', newName
);
233 deleteNetwork: function(network
) {
234 return this.getZones().then(L
.bind(function(zones
) {
237 for (var i
= 0; i
< zones
.length
; i
++)
238 if (zones
[i
].deleteNetwork(network
))
245 getColorForName
: getColorForName
249 AbstractFirewallItem
= L
.Class
.extend({
250 get: function(option
) {
251 return uci
.get('firewall', this.sid
, option
);
254 set: function(option
, value
) {
255 return uci
.set('firewall', this.sid
, option
, value
);
260 Defaults
= AbstractFirewallItem
.extend({
261 __init__: function() {
262 var sections
= uci
.sections('firewall', 'defaults');
264 for (var i
= 0; i
< sections
.length
; i
++) {
265 this.sid
= sections
[i
]['.name'];
269 if (this.sid
== null)
270 this.sid
= uci
.add('firewall', 'defaults');
273 isSynFlood: function() {
274 return (this.get('syn_flood') == '1');
277 isDropInvalid: function() {
278 return (this.get('drop_invalid') == '1');
281 getInput: function() {
282 return parsePolicy(this.get('input'), 'DROP');
285 getOutput: function() {
286 return parsePolicy(this.get('output'), 'DROP');
289 getForward: function() {
290 return parsePolicy(this.get('forward'), 'DROP');
295 Zone
= AbstractFirewallItem
.extend({
296 __init__: function(name
) {
297 var section
= uci
.get('firewall', name
);
299 if (section
!= null && section
['.type'] == 'zone') {
303 else if (name
!= null) {
304 var sections
= uci
.get('firewall', 'zone');
306 for (var i
= 0; i
< sections
.length
; i
++) {
307 if (sections
[i
].name
!= name
)
310 this.sid
= sections
[i
]['.name'];
311 this.data
= sections
[i
];
317 isMasquerade: function() {
318 return (this.get('masq') == '1');
321 getName: function() {
322 return this.get('name');
325 getNetwork: function() {
326 return this.get('network');
329 getInput: function() {
330 return parsePolicy(this.get('input'), (new Defaults()).getInput());
333 getOutput: function() {
334 return parsePolicy(this.get('output'), (new Defaults()).getOutput());
337 getForward: function() {
338 return parsePolicy(this.get('forward'), (new Defaults()).getForward());
341 addNetwork: function(network
) {
342 var section
= uci
.get('network', network
);
344 if (section
== null || section
['.type'] != 'interface')
347 var newNetworks
= this.getNetworks();
349 if (newNetworks
.filter(function(net
) { return net
== network
}).length
)
352 newNetworks
.push(network
);
353 this.set('network', newNetworks
.join(' '));
358 deleteNetwork: function(network
) {
359 var oldNetworks
= this.getNetworks(),
360 newNetworks
= oldNetworks
.filter(function(net
) { return net
!= network
});
362 if (newNetworks
.length
> 0)
363 this.set('network', newNetworks
.join(' '));
365 this.set('network', null);
367 return (newNetworks
.length
< oldNetworks
.length
);
370 getNetworks: function() {
371 return L
.toArray(this.get('network'));
374 clearNetworks: function() {
375 this.set('network', ' ');
378 getDevices: function() {
379 return L
.toArray(this.get('device'));
382 getSubnets: function() {
383 return L
.toArray(this.get('subnet'));
386 getForwardingsBy: function(what
) {
387 var sections
= uci
.sections('firewall', 'forwarding'),
390 for (var i
= 0; i
< sections
.length
; i
++) {
391 if (sections
[i
].src
== null || sections
[i
].dest
== null)
394 if (sections
[i
][what
] != this.getName())
397 forwards
.push(new Forwarding(sections
[i
]['.name']));
403 addForwardingTo: function(dest
) {
404 var forwards
= this.getForwardingsBy('src'),
405 zone
= lookupZone(dest
);
407 if (zone
== null || zone
.getName() == this.getName())
410 for (var i
= 0; i
< forwards
.length
; i
++)
411 if (forwards
[i
].getDestination() == zone
.getName())
414 var sid
= uci
.add('firewall', 'forwarding');
416 uci
.set('firewall', sid
, 'src', this.getName());
417 uci
.set('firewall', sid
, 'dest', zone
.getName());
419 return new Forwarding(sid
);
422 addForwardingFrom: function(src
) {
423 var forwards
= this.getForwardingsBy('dest'),
424 zone
= lookupZone(src
);
426 if (zone
== null || zone
.getName() == this.getName())
429 for (var i
= 0; i
< forwards
.length
; i
++)
430 if (forwards
[i
].getSource() == zone
.getName())
433 var sid
= uci
.add('firewall', 'forwarding');
435 uci
.set('firewall', sid
, 'src', zone
.getName());
436 uci
.set('firewall', sid
, 'dest', this.getName());
438 return new Forwarding(sid
);
441 deleteForwardingsBy: function(what
) {
442 var sections
= uci
.sections('firewall', 'forwarding'),
445 for (var i
= 0; i
< sections
.length
; i
++) {
446 if (sections
[i
].src
== null || sections
[i
].dest
== null)
449 if (sections
[i
][what
] != this.getName())
452 uci
.remove('firewall', sections
[i
]['.name']);
459 deleteForwarding: function(forwarding
) {
460 if (!(forwarding
instanceof Forwarding
))
463 var section
= uci
.get('firewall', forwarding
.sid
);
465 if (!section
|| section
['.type'] != 'forwarding')
468 uci
.remove('firewall', section
['.name']);
473 addRedirect: function(options
) {
474 var sid
= uci
.add('firewall', 'redirect');
476 if (options
!= null && typeof(options
) == 'object')
477 for (var key
in options
)
478 if (options
.hasOwnProperty(key
))
479 uci
.set('firewall', sid
, key
, options
[key
]);
481 uci
.set('firewall', sid
, 'src', this.getName());
483 return new Redirect(sid
);
486 addRule: function(options
) {
487 var sid
= uci
.add('firewall', 'rule');
489 if (options
!= null && typeof(options
) == 'object')
490 for (var key
in options
)
491 if (options
.hasOwnProperty(key
))
492 uci
.set('firewall', sid
, key
, options
[key
]);
494 uci
.set('firewall', sid
, 'src', this.getName());
496 return new Rule(sid
);
499 getColor: function(forName
) {
500 var name
= (arguments
.length
> 0 ? forName
: this.getName());
502 return getColorForName(name
);
507 Forwarding
= AbstractFirewallItem
.extend({
508 __init__: function(sid
) {
512 getSource: function() {
513 return this.get('src');
516 getDestination: function() {
517 return this.get('dest');
520 getSourceZone: function() {
521 return lookupZone(this.getSource());
524 getDestinationZone: function() {
525 return lookupZone(this.getDestination());
530 Rule
= AbstractFirewallItem
.extend({
531 getSource: function() {
532 return this.get('src');
535 getDestination: function() {
536 return this.get('dest');
539 getSourceZone: function() {
540 return lookupZone(this.getSource());
543 getDestinationZone: function() {
544 return lookupZone(this.getDestination());
549 Redirect
= AbstractFirewallItem
.extend({
550 getSource: function() {
551 return this.get('src');
554 getDestination: function() {
555 return this.get('dest');
558 getSourceZone: function() {
559 return lookupZone(this.getSource());
562 getDestinationZone: function() {
563 return lookupZone(this.getDestination());