module(..., package.seeall) -- Load libraries require("modelfunctions") require("fs") require("format") -- Set variables local packagename = "iptables" local servicename = "iptables" local rulesfile = "/var/lib/iptables/rules-save" local path = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin " local tables = {"filter", "nat", "mangle"} local details -- ################################################################################ -- LOCAL FUNCTIONS local getdetails = function() if not details then details = {} local cmd = path .. "iptables -t filter -n -L" for i,tab in ipairs(tables) do local f = io.popen( (string.gsub(cmd, "filter", tab)) ) details[tab] = {table=tab} local file = f:read("*a") f:close() for line in string.gmatch(file, "([^\n]+)") do if string.match(line, "^Chain") then local name = string.match(line, "^%w+%s+(%S+)") local policy = string.match(line, "policy (%w+)") local references = string.match(line, "(%d+) references") table.insert(details[tab], {name=name, policy=policy, references=references}) elseif not string.match(line, "^target%s+prot") then table.insert(details[tab][#details[tab]], line) elseif not details[tab].header then details[tab].header = line end end end end end local find_chain = function(tab, chain) getdetails() if details[tab] then for i,chn in ipairs(details[tab]) do if chain == chn.name then return chn end end end return nil end local save = function() local cmd = path .. "/etc/init.d/"..servicename.." save 2>&1" local f = io.popen( cmd ) f:close() details = nil end -- ################################################################################ -- PUBLIC FUNCTIONS function getstatus() local status = {} local value, errtxt = processinfo.package_version(packagename) status.version = cfe({ label="Program version", value=value, errtxt=errtxt, }) return cfe({ type="group", value=status, label="IPtables Status" }) end function getstatusdetails() getdetails() local retval = {} for i,tab in ipairs(tables) do local chains = 0 local rules = 0 for i,chain in ipairs(details[tab]) do chains = chains + 1 rules = rules + #chain end retval[tab] = {chains=chains, rules=rules} end return cfe({ type="structure", value=retval, label="IPtables Status Details" }) end function getrules(tab) getdetails() tab = tab or "filter" return cfe({ type="structure", value=details[tab] or {}, label=string.gsub(tab, "^.", string.upper).." Rules" }) end function read_chain(tab, chain) local retval = {} retval.table = cfe({ type="select", value=tab or "filter", label="Table", option=tables }) retval.chain = cfe({ value=chain or "", label="Chain" }) getdetails() if tab and not details[tab] then retval.table.errtxt = "Invalid table" end if chain then local chn = find_chain(retval.table.value, chain) if not chn then retval.chain.errtxt = "Cannot find chain" elseif chn.policy then -- only built-in chains can have policies, and the target can only be DROP or ACCEPT retval.policy = cfe({ type="select", value=chn.policy, label="Policy", option={"DROP", "ACCEPT"} }) end end return cfe({ type="group", value=retval, label="Chain" }) end function update_chain(chain) local success = true getdetails() if not details[chain.value.table.value] then chain.value.table.errtxt = "Invalid table" success = false elseif not find_chain(chain.value.table.value, chain.value.chain.value) then chain.value.chain.errtxt = "Invalid chain" success = false end if success then if chain.value.policy then local cmd = path .. "iptables -t "..chain.value.table.value.." -P "..chain.value.chain.value.." "..chain.value.policy.value.." 2>&1" local f = io.popen(cmd) local errtxt = f:read("*a") f:close() if errtxt ~= "" then chain.errtxt = errtxt end save() end else chain.errtxt = "Failed to update chain" end return chain end function create_chain(chain) local success = true getdetails() if not details[chain.value.table.value] then chain.value.table.errtxt = "Invalid table" success = false elseif find_chain(chain.value.table.value, chain.value.chain.value) then chain.value.chain.errtxt = "Chain already exists" success = false end if string.find(chain.value.chain.value, "[%s\'\"]") then chain.value.chain.errtxt = "Chain cannot contain spaces or quotes" success = false end if success then local cmd = path .. "iptables -t "..chain.value.table.value.." -N "..chain.value.chain.value.." 2>&1" local f = io.popen(cmd) local errtxt = f:read("*a") if errtxt ~= "" then chain.errtxt = errtxt end f:close() save() else chain.errtxt = "Failed to create chain" end return chain end function delete_chain(tab, chain) local retval = cfe({ label="Delete Chain result" }) tab = tab or "filter" local chn = find_chain(tab, chain) if not chn then retval.errtxt = "Could not find chain" elseif chn.policy then retval.errtxt = "Cannot delete built-in chain" elseif chn.references and tonumber(chn.references) > 0 then retval.errtxt = "Cannot delete chain with references" else local cmd = path .. "iptables -t "..tab.." -X "..chain.." 2>&1" local f = io.popen(cmd) local errtxt = f:read("*a") if errtxt ~= "" then retval.errtxt = errtxt else retval.value = "Deleted Chain" end save() end return retval end function read_rule(tab, chain, pos) local retval = {} retval.table = cfe({ type="select", value=tab or "filter", label="Table", option=tables }) retval.chain = cfe({ value=chain or "", label="Chain" }) retval.position = cfe({ value=pos or "", label="Position" }) retval.protocol = cfe({ value="all", label="Protocol", descr="One of tcp, udp, icmp, or all, or a numeric value representing a protocol, or a protocol name from /etc/protocols. A '!' before the protocol inverts the test." }) retval.source = cfe({ label="Source", descr="A network name or IP address (may have mask of type /xxx.xxx.xxx.xxx or /xx). A '!' before the address spcification inverts the sense of the address." }) retval.destination = cfe({ label="Destination", descr="A network name or IP address (may have mask of type /xxx.xxx.xxx.xxx or /xx). A '!' before the address spcification inverts the sense of the address." }) retval.jump = cfe({ label="Target", descr="Specify the target of the rule - one of ACCEPT, DROP, QUEUE, or RETURN, or the name of a user-defined chain." }) retval.goto = cfe({ label="Goto", descr="Processing should continue in the specified chain" }) retval.in_interface = cfe({ label="In Interface", descr="Name of an interface via which a packet was received. A '!' before the interface inverts the sense. A '+' ending the interface will match any interface that begins with this name." }) retval.out_interface = cfe({ label="Out Interface", descr="Name of an interface via which a packet is going to be sent. A '!' before the interface inverts the sense. A '+' ending the interface will match any interface that begins with this name." }) retval.fragment = cfe({ label="Fragment", descr="A '+' specifies the second and further packets of fragmented packets. A '!' specifies only head fragments or unfragmented packets." }) retval.set_counters = cfe({ label="Set Counters", descr="'Number, number' to initialize the packet and byte counters."}) getdetails() if tab and not details[tab] then retval.table.errtxt = "Invalid table" end local chn if chain then chn = find_chain(retval.table.value, chain) if not chn then retval.chain.errtxt = "Cannot find chain" end end if pos and chn then if not chn[tonumber(pos)] then retval.position.errtxt = "Cannot find rule" else -- We found the rule, update the settings end end return cfe({ type="group", value=retval, label="Rule" }) end function readrulesfile() return modelfunctions.getfiledetails(rulesfile) end function updaterulesfile(filedetails) return modelfunctions.setfiledetails(filedetails, {rulesfile}) end -- local implementation to add save, reload, and panic actions function startstop_service(action) local cmdresult = cfe({ label="Start/Stop result" }) if (string.lower(action) == "start") or (string.lower(action) == "stop") or (string.lower(action) == "restart") or (string.lower(action) == "save") or (string.lower(action) == "reload") or (string.lower(action) == "panic") then local file = io.popen( "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin /etc/init.d/" .. servicename .. " " .. string.lower(action) .. " 2>&1" ) if file ~= nil then cmdresult.value = file:read( "*a" ) file:close() end posix.sleep(2) -- Wait for the process to start|stop else cmdresult.errtxt = "Unknown command!" end return cmdresult end