diff options
| -rw-r--r-- | iptables-details-html.lsp | 4 | ||||
| -rw-r--r-- | iptables-editrule-html.lsp | 17 | ||||
| -rw-r--r-- | iptables-html.lsp | 12 | ||||
| -rw-r--r-- | iptables-model.lua | 206 | 
4 files changed, 223 insertions, 16 deletions
diff --git a/iptables-details-html.lsp b/iptables-details-html.lsp index bbdbd9a..9edc25d 100644 --- a/iptables-details-html.lsp +++ b/iptables-details-html.lsp @@ -14,8 +14,8 @@ io.write("</span>")  <% for i,tab in ipairs({"filter", "nat", "mangle"}) do %>  	<H3><%= tab %></H3>  	<TABLE> -	<TR><%= data.value[tab].chains %> Chains</TR> -	<TR><%= data.value[tab].rules %> Rules</TR> +	<TR><TD><%= data.value[tab].chains %> Chains</TD></TR> +	<TR><TD><%= data.value[tab].rules %> Rules</TD></TR>  	</TABLE>  <% end %>  </DL> diff --git a/iptables-editrule-html.lsp b/iptables-editrule-html.lsp index 7a7ad3c..8960191 100644 --- a/iptables-editrule-html.lsp +++ b/iptables-editrule-html.lsp @@ -21,15 +21,24 @@ displayformitem(form.value.position)  %>  </DL><H2>Basic Parameters</H2><DL>  <%  +displayformitem(form.value.jump)  displayformitem(form.value.protocol) +displayformitem(form.value.in_interface) +displayformitem(form.value.out_interface)  displayformitem(form.value.source)  displayformitem(form.value.destination) -displayformitem(form.value.jump) +%> +</DL><DL id="details"> +<%  displayformitem(form.value.goto) -displayformitem(form.value.in_interface) -displayformitem(form.value.out_interface)  displayformitem(form.value.fragment)  displayformitem(form.value.set_counters)  %> -</DL><H2>Save</H2> +</DL><H2>Extended Parameters</H2><DL> +<%  +displayformitem(form.value.comment) +displayformitem(form.value.addrtype_src_type) +displayformitem(form.value.addrtype_dst_type) +%> +</DL><H2><%= form.option %></H2>  <% displayformend(form) %> diff --git a/iptables-html.lsp b/iptables-html.lsp index d9600e1..b27216b 100644 --- a/iptables-html.lsp +++ b/iptables-html.lsp @@ -19,16 +19,22 @@  		<% if chain.references then io.write(" ("..chain.references.." references)\n") end %>  		</TD></TR>  		<% for j,line in ipairs(chain) do %> -			<TR><TD STYLE='padding-left:40px'> +			<TABLE> +			<TR><TD WIDTH='80px' STYLE='padding-left:40px'>  			<a href="<%= page_info.script..page_info.prefix..page_info.controller.."/createrule?table="..tab.."&chain="..chain.name.."&position="..j.."&redir="..page_info.orig_action %>"><IMG SRC='/skins/static/tango/16x16/actions/list-add.png' width='16' height='16' title="Insert Rule"></a>  			<a href="<%= page_info.script..page_info.prefix..page_info.controller.."/deleterule?table="..tab.."&chain="..chain.name.."&position="..j.."&redir="..page_info.orig_action %>"><IMG SRC='/skins/static/tango/16x16/actions/list-remove.png' width='16' height='16' title="Delete Rule"></a>  			<a href="<%= page_info.script..page_info.prefix..page_info.controller.."/editrule?table="..tab.."&chain="..chain.name.."&position="..j.."&redir="..page_info.orig_action %>"><IMG SRC='/skins/static/tango/16x16/actions/document-properties.png' width='16' height='16' title="Edit Rule"></a> -			</TD><TD><%= line %></TD> +			</TD> +			<TD WIDTH='50px'><%= line.packets %></TD><TD WIDTH='50px'><%= line.bytes %></TD> +				<TD><%= line.rule %></TD>  			</TR> +			</TABLE>  		<% end %> -		<TR><TD STYLE='padding-left:40px'> +		<TABLE> +		<TR><TD WIDTH='80px' STYLE='padding-left:40px'>  		<a href="<%= page_info.script..page_info.prefix..page_info.controller.."/createrule?table="..tab.."&chain="..chain.name.."&redir="..page_info.orig_action %>"><IMG SRC='/skins/static/tango/16x16/actions/list-add.png' width='16' height='16' title="Append Rule"></a>  		</TD></TR> +		</TABLE>  	<% end %>  	<TR><TD><a href="<%= page_info.script..page_info.prefix..page_info.controller.."/createchain?table="..tab.."&redir="..page_info.orig_action %>"><IMG SRC='/skins/static/tango/16x16/actions/list-add.png' width='16' height='16' title="Create Chain"></a></TD></TR>  	</TABLE> diff --git a/iptables-model.lua b/iptables-model.lua index 3f55965..84e6687 100644 --- a/iptables-model.lua +++ b/iptables-model.lua @@ -4,6 +4,7 @@ module(..., package.seeall)  require("modelfunctions")  require("fs")  require("format") +require("validator")  -- Set variables  local packagename = "iptables" @@ -20,7 +21,7 @@ local details  local getdetails = function()  	if not details then  		details = {} -		local cmd = path .. "iptables -t filter -n -L" +		local cmd = path .. "iptables -t filter -n -L -v"  		for i,tab in ipairs(tables) do  			local f = io.popen( (string.gsub(cmd, "filter", tab)) )  			details[tab] = {table=tab} @@ -32,8 +33,11 @@ local getdetails = function()  					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 string.match(line, "target%s+prot") then +					local block = {} +					block.packets, block.bytes, block.rule = string.match(line, "^%s*(%S+)%s+(%S+)%s+(.*)$") +					table.insert(details[tab][#details[tab]], block) +					--table.insert(details[tab][#details[tab]], line)  				elseif not details[tab].header then  					details[tab].header = line  				end @@ -61,6 +65,93 @@ local save = function()  	details = nil  end +local function validate_rule(rule) +	local success = true + +	function basiccheck(val) +		if string.find(val.value, "[%s\'\"]") then +			val.errtxt = "Cannot contain spaces or quotes" +			success = false +		end +	end + +	success = modelfunctions.validateselect(rule.value.table) and success +	basiccheck(rule.value.chain) +	if rule.value.position.value ~= "" and not validator.is_integer(rule.value.position.value) then +		rule.value.position.errtxt = "Must be a number" +		success = false +	end +	basiccheck(rule.value.protocol) +	basiccheck(rule.value.source) +	basiccheck(rule.value.destination) +	basiccheck(rule.value.jump) +	basiccheck(rule.value.goto) +	basiccheck(rule.value.in_interface) +	basiccheck(rule.value.out_interface) +	if #rule.value.fragment.value > 1 or string.match(rule.value.fragment.value, "[^%+!]") then +		rule.value.fragment.errtxt = "Invalid entry" +		success = false +	end +	if rule.value.set_counters.value ~= "" and not string.match(rule.value.set_counters.value, "^%d+%s+%d+$") then +		rule.value.set_counters.errtxt = "Invalid entry" +		success = false +	end +	basiccheck(rule.value.addrtype_src_type) +	basiccheck(rule.value.addrtype_dst_type) +	if string.find(rule.value.comment.value, "[\'\"]") then +		rule.value.comment.errtxt = "Cannot contain quotes" +		success = false +	end +	if rule.value.jump.value == "" and rule.value.goto.value == "" then +		rule.value.jump.errtxt = "Must define target or goto" +		success = false +	end + +	return success, rule +end + +local function generate_rule_specification(rule) +	local spec = {} + +	function addparameter(value, option) +		if value ~= "" then +			spec[#spec + 1] = option +			spec[#spec + 1] = value +		end +	end +	function addmodule(values, mod) +		for i,value in ipairs(values) do +			if value ~= "" then +				spec[#spec + 1] = "-m "..mod +				break +			end +		end +	end + +	addparameter(rule.value.protocol.value, "-p") +	addparameter(rule.value.source.value, "-s") +	addparameter(rule.value.destination.value, "-d") +	addparameter(rule.value.jump.value, "-j") +	addparameter(rule.value.goto.value, "-g") +	addparameter(rule.value.in_interface.value, "-i") +	addparameter(rule.value.out_interface.value, "-o") +	if rule.value.fragment.value == "!" then +		spec[#spec + 1] = "! -f" +	elseif rule.value.fragment.value ~= "" then +		spec[#spec + 1] = "-f" +	end +	addparameter(rule.value.set_counters.value, "-c") +	addmodule({rule.value.addrtype_src_type.value, rule.value.addrtype_dst_type.value}, "addrtype") +	addparameter(rule.value.addrtype_src_type.value, "--src-type") +	addparameter(rule.value.addrtype_dst_type.value, "--dst-type") +	addmodule({rule.value.comment.value}, "comment") +	if rule.value.comment.value ~= "" then +		addparameter('"' .. rule.value.comment.value .. '"', "--comment") +	end + +	return table.concat(spec, " ") +end +  -- ################################################################################  -- PUBLIC FUNCTIONS @@ -160,7 +251,7 @@ function create_chain(chain)  		success = false  	end  	if string.find(chain.value.chain.value, "[%s\'\"]") then -		chain.value.chain.errtxt = "Chain cannot contain spaces or quotes" +		chain.value.chain.errtxt = "Cannot contain spaces or quotes"  		success = false  	end @@ -207,18 +298,24 @@ end  function read_rule(tab, chain, pos)  	local retval = {} +	-- Identification  	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" }) +	-- Basics  	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.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 specification 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 specification 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."}) +	retval.set_counters = cfe({ label="Set Counters", descr="'Number number' to initialize the packet and byte counters."}) +	-- Extensions +	retval.addrtype_src_type = cfe({ type="select", label="Source Address Type", option={"", "UNSPEC", "UNICAST", "LOCAL", "BROADCAST", "ANYCAST", "MULTICAST", "BLACKHOLE", "UNREACHABLE", "PROHIBIT"} }) +	retval.addrtype_dst_type = cfe({ type="select", label="Destination Address Type", option={"", "UNSPEC", "UNICAST", "LOCAL", "BROADCAST", "ANYCAST", "MULTICAST", "BLACKHOLE", "UNREACHABLE", "PROHIBIT"} }) +	retval.comment = cfe({ label="Comment" })  	getdetails()  	if tab and not details[tab] then @@ -236,11 +333,106 @@ function read_rule(tab, chain, pos)  			retval.position.errtxt = "Cannot find rule"  		else  			-- We found the rule, update the settings +			local words = {} +			for word in string.gmatch(chn[tonumber(pos)].rule, "%S+") do words[#words + 1] = word end +			retval.jump.value = words[1] or "" +			retval.protocol.value = words[2] or "" +			if words[3] == "-f" then +				retval.fragment.value = "+" +			elseif words[3] == "!f" then +				retval.fragment.value = "!" +			end +			retval.in_interface.value = words[4] or "" +			if retval.in_interface.value == "*" then retval.in_interface.value = "" end +			retval.out_interface.value = words[5] or "" +			if retval.out_interface.value == "*" then retval.out_interface.value = "" end +			retval.source.value = words[6] or "" +			retval.destination.value = words[7] or "" + +			local i=8 +			while i <= #words do +				if words[i] == "[goto]" then +					retval.goto.value = retval.jump.value +					retval.jump.value = "" +				elseif words[i] == "src-type" then +					retval.addrtype_src_type.value = words[i+1] +					i = i+1 +				elseif words[i] == "dst-type" then +					retval.addrtype_dst_type.value = words[i+1] +					i = i+1 +				end +				i = i+1 +			end +			retval.comment.value = string.match(chn[tonumber(pos)].rule, "/%*%s+(.*)%s%*/") or ""  		end  	end  	return cfe({ type="group", value=retval, label="Rule" })  end +function create_rule(rule) +	local success, rule = validate_rule(rule) + +	if success then +		local spec = generate_rule_specification(rule) +		local cmd = path .. "iptables -t " .. rule.value.table.value +		if rule.value.position.value ~= "" then +			cmd = cmd .. " -I " .. rule.value.chain.value .. " " .. rule.value.position.value +		else +			cmd = cmd .. " -A " .. rule.value.chain.value +		end +		cmd = cmd .. " " .. spec .. " 2>&1" +		local f = io.popen(cmd) +		rule.errtxt = f:read("*a") +		f:close() +		if string.match(rule.errtxt, "^%s*$") then +			rule.errtxt = nil +		end +	else +		rule.errtxt = "Failed to create rule" +	end + +	return rule +end + +function update_rule(rule) +	local success, rule = validate_rule(rule) +	if rule.value.position.value == "" then +		rule.value.position.errtxt = "Cannot be empty" +		successs = false +	end + +	if success then +		local spec = generate_rule_specification(rule) +		local cmd = path .. "iptables -t " .. rule.value.table.value .. " -R " .. rule.value.chain.value .. " " .. rule.value.position.value .. " " .. spec .. " 2>&1" +		local f = io.popen(cmd) +		rule.errtxt = f:read("*a") +		f:close() +		if string.match(rule.errtxt, "^%s*$") then +			rule.errtxt = nil +		end +	else +		rule.errtxt = "Failed to update rule" +	end + +	return rule +end + +function delete_rule(tab, chain, pos) +	local cmdresult = cfe({ label="Delete Rule Result" }) +	if not tab or not chain or not pos then +		cmdresult.errtxt = "Incomplete specification - must define table, chain, and position" +	else +		local cmd = path .. "iptables -t " .. tab .. " -D " .. chain .. " " .. pos +		local f = io.popen(cmd) +		cmdresult.value = f:read("*a") +		f:close() +		if string.match(cmdresult.value, "^%s*$") then +			cmdresult.value = "Deleted rule" +		end +	end +	return cmdresult +end +  function readrulesfile()  	return modelfunctions.getfiledetails(rulesfile)  end  | 
