From 08dfc28786bab0ad873153236bad1d9a0ea5a97e Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Sat, 30 Oct 2010 04:47:37 +0000 Subject: [PATCH] libs/core: rework firewall model --- libs/core/luasrc/model/firewall.lua | 423 +++++++++++++++++++++++++++--------- 1 file changed, 320 insertions(+), 103 deletions(-) diff --git a/libs/core/luasrc/model/firewall.lua b/libs/core/luasrc/model/firewall.lua index a1daf5a70..1baa2ce23 100644 --- a/libs/core/luasrc/model/firewall.lua +++ b/libs/core/luasrc/model/firewall.lua @@ -22,24 +22,61 @@ local type, pairs, ipairs, table, luci, math local lmo = require "lmo" local utl = require "luci.util" -local uct = require "luci.model.uci.bind" +local uci = require "luci.model.uci" module "luci.model.firewall" -local ub = uct.bind("firewall") +local uci_r, uci_s -function init(cursor) - if cursor then - cursor:unload("firewall") - cursor:load("firewall") - ub:init(cursor) +function _strlist(x) + if x == nil then + x = "" + elseif type(x) == "table" then + x = table.concat(x, " ") + end + + return x:gmatch("%S+") +end + +function _valid_id(x) + return (x and #x > 0 and x:match("^[a-zA-Z0-9_]+$")) +end + +function _get(c, s, o) + return uci_r:get(c, s, o) +end + +function _set(c, s, o, v) + if v ~= nil then + if type(v) == "boolean" then v = v and "1" or "0" end + return uci_r:set(c, s, o, v) + else + return uci_r:del(c, s, o, v) end end + +function init(cursor) + uci_r = cursor or uci_r or uci.cursor() + uci_s = uci_r:substate() + + return _M +end + +function save(self, ...) + uci_r:save(...) + uci_r:load(...) +end + +function commit(self, ...) + uci_r:commit(...) + uci_r:load(...) +end + function add_zone(self, n) - if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not self:get_zone(n) then - local z = ub.uci:section("firewall", "zone", nil, { + if _valid_id(n) and not self:get_zone(n) then + local z = uci_r:section("firewall", "zone", nil, { name = n, network = " ", input = defaults:input() or "DROP", @@ -52,35 +89,48 @@ function add_zone(self, n) end function get_zone(self, n) - local z - ub.uci:foreach("firewall", "zone", - function(s) - if n and s.name == n then - z = s['.name'] - return false - end - end) - return z and zone(z) + if uci_r:get("firewall", n) == "zone" then + return zone(n) + else + local z + uci_r:foreach("firewall", "zone", + function(s) + if n and s.name == n then + z = s['.name'] + return false + end + end) + return z and zone(z) + end end function get_zones(self) local zones = { } - ub.uci:foreach("firewall", "zone", + local znl = { } + + uci_r:foreach("firewall", "zone", function(s) if s.name then - zones[#zones+1] = zone(s['.name']) + znl[s.name] = zone(s['.name']) end end) + + local z + for z in utl.kspairs(znl) do + zones[#zones+1] = znl[z] + end + return zones end function get_zone_by_network(self, net) local z - ub.uci:foreach("firewall", "zone", + + uci_r:foreach("firewall", "zone", function(s) if s.name and net then local n - for _, n in ipairs(ub:list(s.network or s.name)) do + for n in _strlist(s.network or s.name) do if n == net then z = s['.name'] return false @@ -88,75 +138,99 @@ function get_zone_by_network(self, net) end end end) + return z and zone(z) end function del_zone(self, n) local r = false - ub.uci:foreach("firewall", "zone", - function(s) - if n and s.name == n then - r = ub.uci:delete("firewall", s['.name']) - return false - end - end) + + if uci_r:get("firewall", n) == "zone" then + local z = uci_r:get("firewall", n, "name") + r = uci_r:delete("firwall", n) + n = z + else + uci_r:foreach("firewall", "zone", + function(s) + if n and s.name == n then + r = uci_r:delete("firewall", s['.name']) + return false + end + end) + end + if r then - ub.uci:foreach("firewall", "rule", + uci_r:foreach("firewall", "rule", function(s) if s.src == n or s.dest == n then - ub.uci:delete("firewall", s['.name']) + uci_r:delete("firewall", s['.name']) end end) - ub.uci:foreach("firewall", "redirect", + + uci_r:foreach("firewall", "redirect", function(s) if s.src == n then - ub.uci:delete("firewall", s['.name']) + uci_r:delete("firewall", s['.name']) end end) - ub.uci:foreach("firewall", "forwarding", + + uci_r:foreach("firewall", "forwarding", function(s) if s.src == n then - ub.uci:delete("firewall", s['.name']) + uci_r:delete("firewall", s['.name']) end end) end + return r end function rename_zone(self, old, new) local r = false - if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_zone(new) then - ub.uci:foreach("firewall", "zone", + + if _valid_id(new) and not self:get_zone(new) then + uci_r:foreach("firewall", "zone", function(s) if n and s.name == old then - ub.uci:set("firewall", s['.name'], "name", new) + uci_r:set("firewall", s['.name'], "name", new) r = true return false end end) + if r then - ub.uci:foreach("firewall", "rule", + uci_r:foreach("firewall", "rule", function(s) if s.src == old then - ub.uci:set("firewall", s['.name'], "src", new) - elseif s.dest == old then - ub.uci:set("firewall", s['.name'], "dest", new) + uci_r:set("firewall", s['.name'], "src", new) + end + if s.dest == old then + uci_r:set("firewall", s['.name'], "dest", new) end end) - ub.uci:foreach("firewall", "redirect", + + uci_r:foreach("firewall", "redirect", function(s) if s.src == old then - ub.uci:set("firewall", s['.name'], "src", new) + uci_r:set("firewall", s['.name'], "src", new) + end + if s.dest == old then + uci_r:set("firewall", s['.name'], "dest", new) end end) + ub.uci:foreach("firewall", "forwarding", function(s) if s.src == old then ub.uci:set("firewall", s['.name'], "src", new) end + if s.dest == old then + uci_r:set("firewall", s['.name'], "dest", new) + end end) end end + return r end @@ -170,100 +244,205 @@ function del_network(self, net) end -defaults = ub:usection("defaults") -defaults:property_bool("syn_flood") -defaults:property_bool("drop_invalid") -defaults:property("input") -defaults:property("forward") -defaults:property("output") +defaults = utl.class() +function defaults.__init__(self) + uci_r:foreach("firewall", "defaults", + function(s) + self.sid = s['.name'] + return false + end) + + self.sid = self.sid or uci_r:section("firewall", "defaults", nil, { }) +end + +function defaults.get(self, opt) + return _get("firewall", self.sid, opt) +end + +function defaults.set(self, opt, val) + return _set("firewall", self.sid, opt, val) +end + +function defaults.syn_flood(self) + return (self:get("syn_flood") == "1") +end + +function defaults.drop_invalid(self) + return (self:get("drop_invalid") == "1") +end + +function defaults.input(self) + return self:get("input") or "DROP" +end + +function defaults.forward(self) + return self:get("forward") or "DROP" +end + +function defaults.output(self) + return self:get("output") or "DROP" +end + + +zone = utl.class() +function zone.__init__(self, z) + if uci_r:get("firewall", z) == "zone" then + self.sid = z + self.data = uci_r:get_all("firewall", z) + else + uci_r:foreach("firewall", "zone", + function(s) + if s.name == z then + self.sid = s['.name'] + self.data = s + return false + end + end) + end +end + +function zone.get(self, opt) + return _get("firewall", self.sid, opt) +end + +function zone.set(self, opt, val) + return _set("firewall", self.sid, opt, val) +end + +function zone.masq(self) + return (self:get("masq") == "1") +end +function zone.name(self) + return self:get("name") +end -zone = ub:section("zone") -zone:property_bool("masq") -zone:property("name") -zone:property("network") -zone:property("input") -zone:property("forward") -zone:property("output") +function zone.network(self) + return self:get("network") +end + +function zone.input(self) + return self:get("input") or "DROP" +end + +function zone.forward(self) + return self:get("forward") or "DROP" +end + +function zone.output(self) + return self:get("output") or "DROP" +end function zone.add_network(self, net) - if ub.uci:get("network", net) == "interface" then - local networks = ub:list(self:network() or self:name(), net) - if #networks > 0 then - self:network(table.concat(networks, " ")) + if uci_r:get("network", net) == "interface" then + local nets = { } + + local n + for n in _strlist(self:get("network") or self:get("name")) do + if n ~= net then + nets[#nets+1] = n + end + end + + nets[#nets+1] = net + + if #nets > 0 then + self:set("network", table.concat(nets, " ")) else - self:network(" ") + self:set("network", " ") end end end function zone.del_network(self, net) - local networks = ub:list(self:network() or self:name(), nil, net) - if #networks > 0 then - self:network(table.concat(networks, " ")) + local nets = { } + + local n + for n in _strlist(self:get("network") or self:get("name")) do + if n ~= net then + nets[#nets+1] = n + end + end + + if #nets > 0 then + self:set("network", table.concat(nets, " ")) else - self:network(" ") + self:set("network", " ") end end function zone.get_networks(self) - return ub:list(self:network() or self:name()) + local nets = { } + + local n + for n in _strlist(self:get("network") or self:get("name")) do + nets[#nets+1] = n + end + + return nets end function zone.get_forwardings_by(self, what) local name = self:name() local forwards = { } - ub.uci:foreach("firewall", "forwarding", + + uci_r:foreach("firewall", "forwarding", function(s) if s.src and s.dest and s[what] == name then forwards[#forwards+1] = forwarding(s['.name']) end end) + return forwards end -function zone.add_forwarding_to(self, dest, with_mtu_fix) +function zone.add_forwarding_to(self, dest) local exist, forward + for _, forward in ipairs(self:get_forwardings_by('src')) do if forward:dest() == dest then exist = true break end end + if not exist and dest ~= self:name() then - local s = ub.uci:section("firewall", "forwarding", nil, { + local s = uci_r:section("firewall", "forwarding", nil, { src = self:name(), - dest = dest, - mtu_fix = with_mtu_fix and "1" or "0" + dest = dest }) + return s and forwarding(s) end end -function zone.add_forwarding_from(self, src, with_mtu_fix) +function zone.add_forwarding_from(self, src) local exist, forward + for _, forward in ipairs(self:get_forwardings_by('dest')) do if forward:src() == src then exist = true break end end + if not exist and src ~= self:name() then - local s = ub.uci:section("firewall", "forwarding", nil, { + local s = uci_r:section("firewall", "forwarding", nil, { src = src, - dest = self:name(), - mtu_fix = with_mtu_fix and "1" or "0" + dest = self:name() }) + return s and forwarding(s) end end function zone.del_forwardings_by(self, what) local name = self:name() - ub.uci:foreach("firewall", "forwarding", + + uci_r:foreach("firewall", "forwarding", function(s) if s.src and s.dest and s[what] == name then - ub.uci:delete("firewall", s['.name']) + uci_r:delete("firewall", s['.name']) end end) end @@ -271,14 +450,16 @@ end function zone.add_redirect(self, options) options = options or { } options.src = self:name() - local s = ub.uci:section("firewall", "redirect", nil, options) + + local s = uci_r:section("firewall", "redirect", nil, options) return s and redirect(s) end function zone.add_rule(self, options) options = options or { } options.src = self:name() - local s = ub.uci:section("firewall", "rule", nil, options) + + local s = uci_r:section("firewall", "rule", nil, options) return s and rule(s) end @@ -310,10 +491,18 @@ function zone.get_color(self) end -forwarding = ub:section("forwarding") -forwarding:property_bool("mtu_fix") -forwarding:property("src") -forwarding:property("dest") +forwarding = utl.class() +function forwarding.__init__(self, f) + self.sid = f +end + +function forwarding.src(self) + return uci_r:get("firewall", self.sid, "src") +end + +function forwarding.dest(self) + return uci_r:get("firewall", self.sid, "dest") +end function forwarding.src_zone(self) return zone(self:src()) @@ -324,33 +513,61 @@ function forwarding.dest_zone(self) end -rule = ub:section("rule") -rule:property("src") -rule:property("src_ip") -rule:property("src_mac") -rule:property("src_port") -rule:property("dest") -rule:property("dest_ip") -rule:property("dest_port") -rule:property("proto") -rule:property("target") +rule = utl.class() +function rule.__init__(self, f) + self.sid = f +end + +function rule.get(self, opt) + return _get("firewall", self.sid, opt) +end + +function rule.set(self, opt, val) + return _set("firewall", self.sid, opt, val) +end + +function rule.src(self) + return uci_r:get("firewall", self.sid, "src") +end + +function rule.dest(self) + return uci_r:get("firewall", self.sid, "dest") +end function rule.src_zone(self) return zone(self:src()) end +function rule.dest_zone(self) + return zone(self:dest()) +end -redirect = ub:section("redirect") -redirect:property("src") -redirect:property("src_ip") -redirect:property("src_mac") -redirect:property("src_port") -redirect:property("src_dport") -redirect:property("dest_ip") -redirect:property("dest_port") -redirect:property("proto") + +redirect = utl.class() +function redirect.__init__(self, f) + self.sid = f +end + +function redirect.get(self, opt) + return _get("firewall", self.sid, opt) +end + +function redirect.set(self, opt, val) + return _set("firewall", self.sid, opt, val) +end + +function redirect.src(self) + return uci_r:get("firewall", self.sid, "src") +end + +function redirect.dest(self) + return uci_r:get("firewall", self.sid, "dest") +end function redirect.src_zone(self) return zone(self:src()) end +function redirect.dest_zone(self) + return zone(self:dest()) +end -- 2.11.0