luci-0.11: merge outstanding trunk changes
[project/luci.git] / libs / core / luasrc / model / firewall.lua
index 6fc207cac484d95e37749ee6fd31f251d88325a9..a9f6fdb7fc2c4b87d39657b6cd85bb17b89457d3 100644 (file)
@@ -20,31 +20,75 @@ limitations under the License.
 local type, pairs, ipairs, table, luci, math
        = type, pairs, ipairs, table, luci, math
 
-local lmo = require "lmo"
+local tpl = require "luci.template.parser"
 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 _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:delete(c, s, o)
+       end
+end
+
 
 function init(cursor)
-       if cursor then
-               cursor:unload("firewall")
-               cursor:load("firewall")
-               ub: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 get_defaults()
+       return defaults()
+end
+
+function new_zone(self)
+       local name = "newzone"
+       local count = 1
+
+       while self:get_zone(name) do
+               count = count + 1
+               name = "newzone%d" % count
        end
+
+       return self:add_zone(name)
 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 d = defaults()
+               local z = uci_r:section("firewall", "zone", nil, {
                        name    = n,
                        network = " ",
-                       input   = defaults:input()   or "DROP",
-                       forward = defaults:forward() or "DROP",
-                       output  = defaults:output()  or "DROP"
+                       input   = d:input()   or "DROP",
+                       forward = d:forward() or "DROP",
+                       output  = d:output()  or "DROP"
                })
 
                return z and zone(z)
@@ -52,111 +96,151 @@ 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_zones_by_network(self, net)
-       local zones = { }
-       ub.uci:foreach("firewall", "zone",
+function get_zone_by_network(self, net)
+       local z
+
+       uci_r:foreach("firewall", "zone",
                function(s)
-                       if s.name then
+                       if s.name and net then
                                local n
-                               for _, n in ipairs(ub:list(s.network or s.name)) do
+                               for n in utl.imatch(s.network or s.name) do
                                        if n == net then
-                                               zones[#zones+1] = zone(s['.name'])
-                                               return true
+                                               z = s['.name']
+                                               return false
                                        end
                                end
                        end
                end)
-       return zones
+
+       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("firewall", 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'])
+                               if s.src == n or s.dest == n then
+                                       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'])
+                               if s.src == n or s.dest == n then
+                                       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)
+                               if old and s.name == old then
+                                       if not s.network then
+                                               uci_r:set("firewall", s['.name'], "network", old)
+                                       end
+                                       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",
+
+                       uci_r:foreach("firewall", "forwarding",
                                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)
                end
        end
+
        return r
 end
 
@@ -170,105 +254,221 @@ 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
+
+function zone.network(self)
+       return self:get("network")
+end
+
+function zone.input(self)
+       return self:get("input") or defaults():input() or "DROP"
+end
 
+function zone.forward(self)
+       return self:get("forward") or defaults():forward() or "DROP"
+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.output(self)
+       return self:get("output") or defaults():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, " "))
-               else
-                       self:network(" ")
+       if uci_r:get("network", net) == "interface" then
+               local nets = { }
+
+               local n
+               for n in utl.imatch(self:get("network") or self:get("name")) do
+                       if n ~= net then
+                               nets[#nets+1] = n
+                       end
                end
+
+               nets[#nets+1] = net
+
+               _M:del_network(net)
+               self:set("network", table.concat(nets, " "))
        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 utl.imatch(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 utl.imatch(self:get("network") or self:get("name")) do
+               nets[#nets+1] = n
+       end
+
+       return nets
+end
+
+function zone.clear_networks(self)
+       self:set("network", " ")
 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, {
+
+       if not exist and dest ~= self:name() and _valid_id(dest) then
+               local s = uci_r:section("firewall", "forwarding", nil, {
                        src     = self:name(),
-                       dest    = dest,
-                       mtu_fix = with_mtu_fix and true or false
+                       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, {
+
+       if not exist and src ~= self:name() and _valid_id(src) then
+               local s = uci_r:section("firewall", "forwarding", nil, {
                        src     = src,
-                       dest    = self:name(),
-                       mtu_fix = with_mtu_fix and true or false
+                       dest    = self:name()
                })
+
                return s and forwarding(s)
        end
 end
 
+function zone.del_forwardings_by(self, what)
+       local name = self:name()
+
+       uci_r:delete_all("firewall", "forwarding",
+               function(s)
+                       return (s.src and s.dest and s[what] == name)
+               end)
+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
 
@@ -278,7 +478,7 @@ function zone.get_color(self)
        elseif self and self:name() == "wan" then
                return "#f09090"
        elseif self then
-               math.randomseed(lmo.hash(self:name()))
+               math.randomseed(tpl.hash(self:name()))
 
                local r   = math.random(128)
                local g   = math.random(128)
@@ -300,10 +500,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())
@@ -314,33 +522,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