--[[ Iptables file dumper for Alpine Wall Copyright (C) 2012 Kaarle Ritvanen Licensed under the terms of GPL2 ]]-- module(..., package.seeall) require 'lpc' require 'awall.object' require 'awall.util' local class = awall.object.class local families = {inet={cmd='iptables', file='rules-save', procfile='/proc/net/ip_tables_names'}, inet6={cmd='ip6tables', file='rules6-save', procfile='/proc/net/ip6_tables_names'}} builtin = {filter={'FORWARD', 'INPUT', 'OUTPUT'}, mangle={'FORWARD', 'INPUT', 'OUTPUT', 'POSTROUTING', 'PREROUTING'}, nat={'INPUT', 'OUTPUT', 'POSTROUTING', 'PREROUTING'}, raw={'OUTPUT', 'PREROUTING'}, security={'FORWARD', 'INPUT', 'OUTPUT'}} local backupdir = '/var/run/awall' local BaseIPTables = class(awall.object.Object) function BaseIPTables:print() for family, tbls in pairs(families) do self:dumpfile(family, io.stdout) print() end end function BaseIPTables:dump(dir) for family, tbls in pairs(families) do local file = io.output(dir..'/'..families[family].file) self:dumpfile(family, file) file:close() end end function BaseIPTables:restore(test) local disabled = true for family, params in pairs(families) do local file = io.open(params.procfile) if file then io.close(file) local pid, stdin, stdout = lpc.run(params.cmd..'-restore', unpack({test and '-t' or nil})) stdout:close() self:dumpfile(family, stdin) stdin:close() assert(lpc.wait(pid) == 0) disabled = false elseif test then io.stderr:write('Warning: '..family..' rules not tested\n') end end if disabled then error('Firewall not enabled in kernel') end end function BaseIPTables:activate() flush() self:restore(false) end function BaseIPTables:test() self:restore(true) end IPTables = class(BaseIPTables) function IPTables:init() self.config = {} setmetatable(self.config, {__index=function(t, k) t[k] = {} setmetatable(t[k], getmetatable(t)) return t[k] end}) end function IPTables:dumpfile(family, iptfile) iptfile:write('# '..families[family].file..' generated by awall\n') for tbl, chains in pairs(self.config[family]) do iptfile:write('*'..tbl..'\n') for chain, rules in pairs(chains) do local policy = '-' if awall.util.contains(builtin[tbl], chain) then policy = tbl == 'filter' and 'DROP' or 'ACCEPT' end iptfile:write(':'..chain..' '..policy..' [0:0]\n') end for chain, rules in pairs(chains) do for i, rule in ipairs(rules) do iptfile:write('-A '..chain..' '..rule..'\n') end end iptfile:write('COMMIT\n') end end local Current = class(BaseIPTables) function Current:dumpfile(family, iptfile) local pid, stdin, stdout = lpc.run(families[family].cmd..'-save') stdin:close() for line in stdout:lines() do iptfile:write(line..'\n') end stdout:close() assert(lpc.wait(pid) == 0) end local Backup = class(BaseIPTables) function Backup:dumpfile(family, iptfile) for line in io.lines(backupdir..'/'..families[family].file) do iptfile:write(line..'\n') end end function backup() Current.new():dump(backupdir) end function revert() Backup.new():activate() end function flush() local empty = IPTables.new() for family, params in pairs(families) do local success, lines = pcall(io.lines, params.procfile) if success then for tbl in lines do for i, chain in ipairs(builtin[tbl]) do empty.config[family][tbl][chain] = {} end end end end empty:restore(false) end