From d37e751caf7d9bf9e33a2837d38e2263c4f370f4 Mon Sep 17 00:00:00 2001 From: Ted Trask Date: Fri, 7 Nov 2008 15:01:13 +0000 Subject: Implemented iptables edit/create/delete rule. Doesn't support all parameters yet. Modified list rules to include more info. git-svn-id: svn://svn.alpinelinux.org/acf/iptables/trunk@1581 ab2d0c66-481e-0410-8bed-d214d4d58bed --- iptables-details-html.lsp | 4 +- iptables-editrule-html.lsp | 17 +++- iptables-html.lsp | 12 ++- 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("") <% for i,tab in ipairs({"filter", "nat", "mangle"}) do %>

<%= tab %>

- <%= data.value[tab].chains %> Chains - <%= data.value[tab].rules %> Rules + +
<%= data.value[tab].chains %> Chains
<%= data.value[tab].rules %> Rules
<% end %> 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) %>

Basic Parameters

<% +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) +%> +
+<% displayformitem(form.value.goto) -displayformitem(form.value.in_interface) -displayformitem(form.value.out_interface) displayformitem(form.value.fragment) displayformitem(form.value.set_counters) %> -

Save

+

Extended Parameters

+<% +displayformitem(form.value.comment) +displayformitem(form.value.addrtype_src_type) +displayformitem(form.value.addrtype_dst_type) +%> +

<%= form.option %>

<% 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 %> <% for j,line in ipairs(chain) do %> - + + + + + +
- <%= line %><%= line.packets %><%= line.bytes %><%= line.rule %>
<% end %> - + + +
<% end %> 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 -- cgit v1.2.3