libs/sys: don't convert searched target to uppercase in IptParser, breaks searches...
[project/luci.git] / libs / sys / luasrc / sys / iptparser.lua
index 484a16f..84589e5 100644 (file)
@@ -1,8 +1,8 @@
 --[[
 
 Iptables parser and query library
-(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
-(c) 2008 Steven Barth <steven@midlink.org>
+(c) 2008-2009 Jo-Philipp Wich <xm@leipzig.freifunk.net>
+(c) 2008-2009 Steven Barth <steven@midlink.org>
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ luci.util   = require "luci.util"
 luci.sys    = require "luci.sys"
 luci.ip     = require "luci.ip"
 
+local tonumber, ipairs, table = tonumber, ipairs, table
+
 --- LuCI iptables parser and query library
 -- @cstyle     instance
 module("luci.sys.iptparser")
@@ -30,8 +32,8 @@ module("luci.sys.iptparser")
 IptParser = luci.util.class()
 
 function IptParser.__init__( self, ... )
-       self._rules = { }
-       self._chain = nil
+       self._rules  = { }
+       self._chains = { }
        self:_parse_rules()
 end
 
@@ -107,7 +109,7 @@ function IptParser.find( self, args )
                local match = true
 
                -- match table
-               if not ( not args.table or args.table == rule.table ) then
+               if not ( not args.table or args.table:lower() == rule.table ) then
                        match = false
                end
 
@@ -120,7 +122,7 @@ function IptParser.find( self, args )
 
                -- match target
                if not ( match == true and (
-                       not args.target or args.target:upper() == rule.target
+                       not args.target or args.target == rule.target
                ) ) then
                        match = false
                end
@@ -182,7 +184,7 @@ function IptParser.find( self, args )
 
                -- insert match
                if match == true then
-                       table.insert( rv, rule )
+                       rv[#rv+1] = rule
                end
        end
 
@@ -200,23 +202,88 @@ function IptParser.resync( self )
 end
 
 
+--- Find the names of all chains within the given table name.
+-- @param table        String containing the table name
+-- @return             Table of chain names in the order they occur.
+function IptParser.chains( self, table )
+       local lookup = { }
+       local chains = { }
+       for _, r in ipairs(self:find({table=table})) do
+               if not lookup[r.chain] then
+                       lookup[r.chain]   = true
+                       chains[#chains+1] = r.chain
+               end
+       end
+       return chains
+end
+
+
+--- Return the given firewall chain within the given table name.
+-- @param table        String containing the table name
+-- @param chain        String containing the chain name
+-- @return             Table containing the fields "policy", "packets", "bytes"
+--                             and "rules". The "rules" field is a table of rule tables.
+function IptParser.chain( self, table, chain )
+       return self._chains[table:lower()] and self._chains[table:lower()][chain]
+end
+
+
+--- Test whether the given target points to a custom chain.
+-- @param target       String containing the target action
+-- @return                     Boolean indicating whether target is a custom chain.
+function IptParser.is_custom_target( self, target )
+       for _, r in ipairs(self._rules) do
+               if r.chain == target then
+                       return true
+               end
+       end
+       return false
+end
+
+
 -- [internal] Parse iptables output from all tables.
 function IptParser._parse_rules( self )
 
        for i, tbl in ipairs({ "filter", "nat", "mangle" }) do
 
+               self._chains[tbl] = { }
+
                for i, rule in ipairs(luci.util.execl("iptables -t " .. tbl .. " --line-numbers -nxvL")) do
 
                        if rule:find( "Chain " ) == 1 then
 
-                               self._chain = rule:gsub("Chain ([^%s]*) .*", "%1")
+                               local crefs
+                               local cname, cpol, cpkt, cbytes = rule:match(
+                                       "Chain ([^%s]*) %(policy (%w+) " ..
+                                       "(%d+) packets, (%d+) bytes%)"
+                               )
+
+                               if not cname then
+                                       cname, crefs = rule:match(
+                                               "Chain ([^%s]*) %((%d+) references%)"
+                                       )
+                               end
+
+                               self._chain = cname
+                               self._chains[tbl][cname] = {
+                                       policy     = cpol,
+                                       packets    = tonumber(cpkt or 0),
+                                       bytes      = tonumber(cbytes or 0),
+                                       references = tonumber(crefs or 0),
+                                       rules      = { }
+                               }
 
                        else
                                if rule:find("%d") == 1 then
 
-                                       local rule_partmays   = luci.util.split( rule, "%s+", nil, true )
+                                       local rule_parts   = luci.util.split( rule, "%s+", nil, true )
                                        local rule_details = { }
 
+                                       -- cope with rules that have no target assigned
+                                       if rule:match("^%d+%s+%d+%s+%d+%s%s") then
+                                               table.insert(rule_parts, 4, nil)
+                                       end
+
                                        rule_details["table"]       = tbl
                                        rule_details["chain"]       = self._chain
                                        rule_details["index"]       = tonumber(rule_parts[1])
@@ -235,7 +302,11 @@ function IptParser._parse_rules( self )
                                                rule_details["options"][i-10] = rule_parts[i]
                                        end
 
-                                       table.insert( self._rules, rule_details )
+                                       self._rules[#self._rules+1] = rule_details
+
+                                       self._chains[tbl][self._chain].rules[
+                                               #self._chains[tbl][self._chain].rules + 1
+                                       ] = rule_details
                                end
                        end
                end