diff options
author | Jo-Philipp Wich <jo@mein.io> | 2019-05-28 17:49:00 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2019-07-07 15:36:25 +0200 |
commit | 79be2d57cde7a360a1004ebf5980caf2bd969212 (patch) | |
tree | 8ce84b357b2dc057ca30f3a67b39d401c7cbaf78 /modules/luci-base/htdocs/luci-static | |
parent | eecf859b2943f19f8fb1f2ad4a2dbe97ce2e2960 (diff) |
luci-base: add client-side implementation of luci.model.firewall
Introduce firewall.js, a client side reimplementation of the
luci.model.firewall class.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'modules/luci-base/htdocs/luci-static')
-rw-r--r-- | modules/luci-base/htdocs/luci-static/resources/firewall.js | 575 |
1 files changed, 575 insertions, 0 deletions
diff --git a/modules/luci-base/htdocs/luci-static/resources/firewall.js b/modules/luci-base/htdocs/luci-static/resources/firewall.js new file mode 100644 index 0000000000..e99f8b83bc --- /dev/null +++ b/modules/luci-base/htdocs/luci-static/resources/firewall.js @@ -0,0 +1,575 @@ +'use strict'; +'require uci'; +'require rpc'; +'require tools.prng as random'; + + +function initFirewallState() { + return uci.load('firewall'); +} + +function toArray(val) { + if (val == null) + return []; + + if (Array.isArray(val)) + return val; + + var s = String(val).trim(); + + if (s == '') + return []; + + return s.split(/\s+/); +} + +function parseEnum(s, values) { + if (s == null) + return null; + + s = String(s).toUpperCase(); + + if (s == '') + return null; + + for (var i = 0; i < values.length; i++) + if (values[i].toUpperCase().indexOf(s) == 0) + return values[i]; + + return null; +} + +function parsePolicy(s, defaultValue) { + return parseEnum(s, ['DROP', 'REJECT', 'ACCEPT']) || (arguments.length < 2 ? null : defaultValue); +} + + +var Firewall, AbstractFirewallItem, Defaults, Zone, Forwarding, Redirect, Rule; + +function lookupZone(name) { + var z = uci.get('firewall', name); + + if (z != null && z['.type'] == 'zone') + return new Zone(z['.name']); + + var sections = uci.sections('firewall', 'zone'); + + for (var i = 0; i < sections.length; i++) { + if (sections[i].name != name) + continue; + + return new Zone(sections[i]['.name']); + } + + return null; +} + +function getColorForName(forName) { + if (forName == null) + return '#eeeeee'; + else if (forName == 'lan') + return '#90f090'; + else if (forName == 'wan') + return '#f09090'; + + random.seed(parseInt(sfh(forName), 16)); + + var r = random.get(128), + g = random.get(128), + min = 0, + max = 128; + + if ((r + g) < 128) + min = 128 - r - g; + else + max = 255 - r - g; + + var b = min + Math.floor(random.get() * (max - min)); + + return '#%02x%02x%02x'.format(0xff - r, 0xff - g, 0xff - b); +} + + +Firewall = L.Class.extend({ + getDefaults: function() { + return initFirewallState().then(function() { + return new Defaults(); + }); + }, + + newZone: function() { + return initFirewallState().then(L.bind(function() { + var name = 'newzone', + count = 1; + + while (this.getZone(name) != null) + name = 'newzone%d'.format(++count); + + return this.addZone(name); + }, this)); + }, + + addZone: function(name) { + return initFirewallState().then(L.bind(function() { + if (name == null || !/^[a-zA-Z0-9_]+$/.test(name)) + return null; + + if (this.getZone(name) != null) + return null; + + var d = new Defaults(), + z = uci.add('firewall', 'zone'); + + uci.set('firewall', z, 'name', name); + uci.set('firewall', z, 'network', ' '); + uci.set('firewall', z, 'input', d.getInput() || 'DROP'); + uci.set('firewall', z, 'output', d.getOutput() || 'DROP'); + uci.set('firewall', z, 'forward', d.getForward() || 'DROP'); + + return new Zone(z); + }, this)); + }, + + getZone: function(name) { + return initFirewallState().then(function() { + return lookupZone(name); + }); + }, + + getZones: function() { + return initFirewallState().then(function() { + var sections = uci.sections('firewall', 'zone'), + zones = []; + + for (var i = 0; i < sections.length; i++) + zones.push(new Zone(sections[i]['.name'])); + + zones.sort(function(a, b) { return a.getName() > b.getName() }); + + return zones; + }); + }, + + getZoneByNetwork: function(network) { + return initFirewallState().then(function() { + var sections = uci.sections('firewall', 'zone'); + + for (var i = 0; i < sections.length; i++) + if (toArray(sections[i].network || sections[i].name).indexOf(network) != -1) + return new Zone(sections[i]['.name']); + + return null; + }); + }, + + deleteZone: function(name) { + return initFirewallState().then(function() { + var section = uci.get('firewall', name), + found = false; + + if (section != null && section['.type'] == 'zone') { + found = true; + name = zone.name; + uci.remove('firewall', zone['.name']); + } + else if (name != null) { + var sections = uci.sections('firewall', 'zone'); + + for (var i = 0; i < sections.length; i++) { + if (sections[i].name != name) + continue; + + found = true; + uci.remove('firewall', sections[i]['.name']); + } + } + + if (found == true) { + sections = uci.sections('firewall'); + + for (var i = 0; i < sections.length; i++) { + if (sections[i]['.type'] != 'rule' && + sections[i]['.type'] != 'redirect' && + sections[i]['.type'] != 'forwarding') + continue; + + if (sections[i].src == name || sections[i].dest == name) + uci.remove('firewall', sections[i]['.name']); + } + } + + return found; + }); + }, + + renameZone: function(oldName, newName) { + return initFirewallState().then(L.bind(function() { + if (oldName == null || newName == null || !/^[a-zA-Z0-9_]+$/.test(newName)) + return false; + + if (lookupZone(newName) != null) + return false; + + var sections = uci.sections('firewall', 'zone'), + found = false; + + for (var i = 0; i < sections.length; i++) { + if (sections[i].name != oldName) + continue; + + if (toArray(sections[i].network).length == 0) + uci.set('firewall', sections[i]['.name'], 'network', oldName); + + uci.set('firewall', sections[i]['.name'], 'name', newName); + found = true; + } + + if (found == true) { + sections = uci.sections('firewall'); + + for (var i = 0; i < sections.length; i++) { + if (sections[i]['.type'] != 'rule' && + sections[i]['.type'] != 'redirect' && + sections[i]['.type'] != 'forwarding') + continue; + + if (sections[i].src == oldName) + uci.set('firewall', sections[i]['.name'], 'src', newName); + + if (sections[i].dest == oldName) + uci.set('firewall', sections[i]['.name'], 'dest', newName); + } + } + + return found; + }, this)); + }, + + deleteNetwork: function(network) { + return this.getZones().then(L.bind(function(zones) { + var rv = false; + + for (var i = 0; i < zones.length; i++) + if (zones[i].deleteNetwork(network)) + rv = true; + + return rv; + }, this)); + }, + + getColorForName: getColorForName +}); + + +AbstractFirewallItem = L.Class.extend({ + get: function(option) { + return uci.get('firewall', this.sid, option); + }, + + set: function(option, value) { + return uci.set('firewall', this.sid, option, value); + } +}); + + +Defaults = AbstractFirewallItem.extend({ + __init__: function() { + var sections = uci.sections('firewall', 'defaults'); + + for (var i = 0; i < sections.length; i++) { + this.sid = sections[i]['.name']; + break; + } + + if (this.sid == null) + this.sid = uci.add('firewall', 'defaults'); + }, + + isSynFlood: function() { + return (this.get('syn_flood') == '1'); + }, + + isDropInvalid: function() { + return (this.get('drop_invalid') == '1'); + }, + + getInput: function() { + return parsePolicy(this.get('input'), 'DROP'); + }, + + getOutput: function() { + return parsePolicy(this.get('output'), 'DROP'); + }, + + getForward: function() { + return parsePolicy(this.get('forward'), 'DROP'); + } +}); + + +Zone = AbstractFirewallItem.extend({ + __init__: function(name) { + var section = uci.get('firewall', name); + + if (section != null && section['.type'] == 'zone') { + this.sid = name; + this.data = section; + } + else if (name != null) { + var sections = uci.get('firewall', 'zone'); + + for (var i = 0; i < sections.length; i++) { + if (sections[i].name != name) + continue; + + this.sid = sections[i]['.name']; + this.data = sections[i]; + break; + } + } + }, + + isMasquerade: function() { + return (this.get('masq') == '1'); + }, + + getName: function() { + return this.get('name'); + }, + + getNetwork: function() { + return this.get('network'); + }, + + getInput: function() { + return parsePolicy(this.get('input'), (new Defaults()).getInput()); + }, + + getOutput: function() { + return parsePolicy(this.get('output'), (new Defaults()).getOutput()); + }, + + getForward: function() { + return parsePolicy(this.get('forward'), (new Defaults()).getForward()); + }, + + addNetwork: function(network) { + var section = uci.get('network', network); + + if (section == null || section['.type'] != 'interface') + return false; + + var newNetworks = this.getNetworks(); + + if (newNetworks.filter(function(net) { return net == network }).length) + return false; + + newNetworks.push(network); + this.set('network', newNetworks.join(' ')); + + return true; + }, + + deleteNetwork: function(network) { + var oldNetworks = this.getNetworks(), + newNetworks = oldNetworks.filter(function(net) { return net != network }); + + if (newNetworks.length > 0) + this.set('network', newNetworks.join(' ')); + else + this.set('network', ' '); + + return (newNetworks.length < oldNetworks.length); + }, + + getNetworks: function() { + return toArray(this.get('network') || this.get('name')); + }, + + clearNetworks: function() { + this.set('network', ' '); + }, + + getForwardingsBy: function(what) { + var sections = uci.sections('firewall', 'forwarding'), + forwards = []; + + for (var i = 0; i < sections.length; i++) { + if (sections[i].src == null || sections[i].dest == null) + continue; + + if (sections[i][what] != this.getName()) + continue; + + forwards.push(new Forwarding(sections[i]['.name'])); + } + + return forwards; + }, + + addForwardingTo: function(dest) { + var forwards = this.getForwardingsBy('src'), + zone = lookupZone(dest); + + if (zone == null || zone.getName() == this.getName()) + return null; + + for (var i = 0; i < forwards.length; i++) + if (forwards[i].getDestination() == zone.getName()) + return null; + + var sid = uci.add('firewall', 'forwarding'); + + uci.set('firewall', sid, 'src', this.getName()); + uci.set('firewall', sid, 'dest', zone.getName()); + + return new Forwarding(sid); + }, + + addForwardingFrom: function(src) { + var forwards = this.getForwardingsBy('dest'), + zone = lookupZone(src); + + if (zone == null || zone.getName() == this.getName()) + return null; + + for (var i = 0; i < forwards.length; i++) + if (forwards[i].getSource() == zone.getName()) + return null; + + var sid = uci.add('firewall', 'forwarding'); + + uci.set('firewall', sid, 'src', zone.getName()); + uci.set('firewall', sid, 'dest', this.getName()); + + return new Forwarding(sid); + }, + + deleteForwardingsBy: function(what) { + var sections = uci.sections('firewall', 'forwarding'), + found = false; + + for (var i = 0; i < sections.length; i++) { + if (sections[i].src == null || sections[i].dest == null) + continue; + + if (sections[i][what] != this.getName()) + continue; + + uci.remove('firewall', sections[i]['.name']); + found = true; + } + + return found; + }, + + deleteForwarding: function(forwarding) { + if (!(forwarding instanceof Forwarding)) + return false; + + var section = uci.get('firewall', forwarding.sid); + + if (!section || section['.type'] != 'forwarding') + return false; + + uci.remove('firewall', section['.name']); + + return true; + }, + + addRedirect: function(options) { + var sid = uci.add('firewall', 'redirect'); + + if (options != null && typeof(options) == 'object') + for (var key in options) + if (options.hasOwnProperty(key)) + uci.set('firewall', sid, key, options[key]); + + uci.set('firewall', sid, 'src', this.getName()); + + return new Redirect(sid); + }, + + addRule: function(options) { + var sid = uci.add('firewall', 'rule'); + + if (options != null && typeof(options) == 'object') + for (var key in options) + if (options.hasOwnProperty(key)) + uci.set('firewall', sid, key, options[key]); + + uci.set('firewall', sid, 'src', this.getName()); + + return new Redirect(sid); + }, + + getColor: function(forName) { + var name = (arguments.length > 0 ? forName : this.getName()); + + return getColorForName(name); + } +}); + + +Forwarding = AbstractFirewallItem.extend({ + __init__: function(sid) { + this.sid = sid; + }, + + getSource: function() { + return this.get('src'); + }, + + getDestination: function() { + return this.get('dest'); + }, + + getSourceZone: function() { + return lookupZone(this.getSource()); + }, + + getDestinationZone: function() { + return lookupZone(this.getDestination()); + } +}); + + +Rule = AbstractFirewallItem.extend({ + getSource: function() { + return this.get('src'); + }, + + getDestination: function() { + return this.get('dest'); + }, + + getSourceZone: function() { + return lookupZone(this.getSource()); + }, + + getDestinationZone: function() { + return lookupZone(this.getDestination()); + } +}); + + +Redirect = AbstractFirewallItem.extend({ + getSource: function() { + return this.get('src'); + }, + + getDestination: function() { + return this.get('dest'); + }, + + getSourceZone: function() { + return lookupZone(this.getSource()); + }, + + getDestinationZone: function() { + return lookupZone(this.getDestination()); + } +}); + + +return Firewall; |