applications/luci-firewall: complete rework firewall ui

- split zone setup, port forwards, traffic rules and firewall.user
	- add quickadd forms for various common rules like port forwards
	- add tool class for textual formatting and descriptions of rules
	- simplify controller, remove old mini admin remainders
This commit is contained in:
Jo-Philipp Wich 2011-12-19 21:16:31 +00:00
parent 24c4cce3ae
commit 033de64a0f
16 changed files with 1464 additions and 572 deletions

View file

@ -0,0 +1,23 @@
module("luci.controller.firewall", package.seeall)
function index()
entry({"admin", "network", "firewall"},
alias("admin", "network", "firewall", "zones"),
_("Firewall"), 60).i18n = "firewall"
entry({"admin", "network", "firewall", "zones"},
arcombine(cbi("firewall/zones"), cbi("firewall/zone-details")),
_("General Settings"), 10).leaf = true
entry({"admin", "network", "firewall", "forwards"},
arcombine(cbi("firewall/forwards"), cbi("firewall/forward-details")),
_("Port Forwards"), 20).leaf = true
entry({"admin", "network", "firewall", "rules"},
arcombine(cbi("firewall/rules"), cbi("firewall/rule-details")),
_("Traffic Rules"), 30).leaf = true
entry({"admin", "network", "firewall", "custom"},
cbi("firewall/custom"),
_("Custom Rules"), 40).leaf = true
end

View file

@ -1,10 +0,0 @@
module("luci.controller.luci_fw.luci_fw", package.seeall)
function index()
entry({"admin", "network", "firewall"}, alias("admin", "network", "firewall", "zones"), _("Firewall"), 60).i18n = "firewall"
entry({"admin", "network", "firewall", "zones"}, arcombine(cbi("luci_fw/zones"), cbi("luci_fw/zone")), nil, 10).leaf = true
entry({"admin", "network", "firewall", "rule"}, arcombine(cbi("luci_fw/zones"), cbi("luci_fw/trule")), nil, 20).leaf = true
entry({"admin", "network", "firewall", "redirect"}, arcombine(cbi("luci_fw/zones"), cbi("luci_fw/rrule")), nil, 30).leaf = true
entry({"mini", "network", "portfw"}, cbi("luci_fw/miniportfw", {autoapply=true}), _("Port forwarding"), 70).i18n = "firewall"
end

View file

@ -0,0 +1,38 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local fs = require "nixio.fs"
local f = SimpleForm("firewall",
translate("Firewall - Custom Rules"),
translate("Custom rules allow you to execute arbritary iptables commands \
which are not otherwise covered by the firewall framework. \
The commands are executed after each firewall restart, right after \
the default ruleset has been loaded."))
local o = f:field(Value, "_custom")
o.template = "cbi/tvalue"
o.rows = 20
function o.cfgvalue(self, section)
return fs.readfile("/etc/firewall.user")
end
function o.write(self, section, value)
value = value:gsub("\r\n?", "\n")
fs.writefile("/etc/firewall.user", value)
end
return f

View file

@ -1,8 +1,7 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2010 Jo-Philipp Wich <xm@subsignal.org>
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -18,18 +17,25 @@ local dsp = require "luci.dispatcher"
arg[1] = arg[1] or ""
m = Map("firewall", translate("Traffic Redirection"),
translate("Traffic redirection allows you to change the " ..
"destination address of forwarded packets."))
m = Map("firewall",
translate("Firewall - Port Forwards"),
translate("This page allows you to change advanced properties of the port \
forwarding entry. In most cases there is no need to modify \
those settings."))
m.redirect = dsp.build_url("admin", "network", "firewall")
m.redirect = dsp.build_url("admin/network/firewall/forwards")
if not m.uci:get(arg[1]) == "redirect" then
if m.uci:get("firewall", arg[1]) ~= "redirect" then
luci.http.redirect(m.redirect)
return
else
local name = m:get(arg[1], "_name")
if not name or #name == 0 then
name = translate("(Unnamed Entry)")
end
m.title = "%s - %s" %{ translate("Firewall - Port Forwards"), name }
end
local has_v2 = nixio.fs.access("/lib/firewall/fw.sh")
local wan_zone = nil
m.uci:foreach("firewall", "zone",
@ -57,30 +63,36 @@ name = s:taboption("general", Value, "_name", translate("Name"))
name.rmempty = true
name.size = 10
src = s:taboption("general", Value, "src", translate("Source zone"))
src = s:taboption("advanced", Value, "src", translate("Source zone"))
src.nocreate = true
src.default = "wan"
src.template = "cbi/firewall_zonelist"
proto = s:taboption("general", Value, "proto", translate("Protocol"))
proto.optional = true
proto:value("tcpudp", "TCP+UDP")
proto:value("tcp udp", "TCP+UDP")
proto:value("tcp", "TCP")
proto:value("udp", "UDP")
proto:value("icmp", "ICMP")
function proto.cfgvalue(...)
local v = Value.cfgvalue(...)
if not v or v == "tcpudp" then
return "tcp udp"
end
return v
end
dport = s:taboption("general", Value, "src_dport", translate("External port"),
translate("Match incoming traffic directed at the given " ..
"destination port or port range on this host"))
dport.datatype = "portrange"
dport:depends("proto", "tcp")
dport:depends("proto", "udp")
dport:depends("proto", "tcpudp")
to = s:taboption("general", Value, "dest_ip", translate("Internal IP address"),
translate("Redirect matched incoming traffic to the specified " ..
"internal host"))
to.datatype = "ip4addr"
for i, dataset in ipairs(luci.sys.net.arptable()) do
for i, dataset in ipairs(sys.net.arptable()) do
to:value(dataset["IP address"])
end
@ -90,13 +102,6 @@ toport = s:taboption("general", Value, "dest_port", translate("Internal port (op
toport.optional = true
toport.placeholder = "0-65535"
toport.datatype = "portrange"
toport:depends("proto", "tcp")
toport:depends("proto", "udp")
toport:depends("proto", "tcpudp")
target = s:taboption("advanced", ListValue, "target", translate("Redirection type"))
target:value("DNAT")
target:value("SNAT")
dest = s:taboption("advanced", Value, "dest", translate("Destination zone"))
dest.nocreate = true
@ -105,34 +110,32 @@ dest.template = "cbi/firewall_zonelist"
src_dip = s:taboption("advanced", Value, "src_dip",
translate("Intended destination address"),
translate(
"For DNAT, match incoming traffic directed at the given destination "..
"ip address. For SNAT rewrite the source address to the given address."
))
translate("Only match incoming traffic directed at the given IP address."))
src_dip.optional = true
src_dip.datatype = "ip4addr"
src_dip.placeholder = translate("any")
src_mac = s:taboption("advanced", Value, "src_mac", translate("Source MAC address"))
src_mac = s:taboption("advanced", DynamicList, "src_mac",
translate("Source MAC address"),
translate("Only match incoming traffic from these MACs."))
src_mac.optional = true
src_mac.datatype = "macaddr"
src_mac.placeholder = translate("any")
src_ip = s:taboption("advanced", Value, "src_ip", translate("Source IP address"))
src_ip = s:taboption("advanced", Value, "src_ip",
translate("Source IP address"),
translate("Only match incoming traffic from this IP or range."))
src_ip.optional = true
src_ip.datatype = "neg_ip4addr"
src_ip.datatype = "neg(ip4addr)"
src_ip.placeholder = translate("any")
sport = s:taboption("advanced", Value, "src_port", translate("Source port"),
translate("Match incoming traffic originating from the given " ..
"source port or port range on the client host"))
sport = s:taboption("advanced", Value, "src_port",
translate("Source port"),
translate("Only match incoming traffic originating from the given source port or port range on the client host"))
sport.optional = true
sport.datatype = "portrange"
sport.placeholder = "0-65536"
sport:depends("proto", "tcp")
sport:depends("proto", "udp")
sport:depends("proto", "tcpudp")
sport.placeholder = translate("any")
reflection = s:taboption("advanced", Flag, "reflection", translate("Enable NAT Loopback"))
reflection.rmempty = true

View file

@ -0,0 +1,141 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local ds = require "luci.dispatcher"
local ft = require "luci.tools.firewall"
m = Map("firewall", translate("Firewall - Port Forwards"),
translate("Port forwarding allows remote computers on the Internet to \
connect to a specific computer or service within the \
private LAN."))
--
-- Port Forwards
--
s = m:section(TypedSection, "redirect", translate("Port Forwards"))
s.template = "cbi/tblsection"
s.addremove = true
s.anonymous = true
s.sortable = true
s.extedit = ds.build_url("admin/network/firewall/forwards/%s")
s.template_addremove = "firewall/cbi_addforward"
function s.create(self, section)
local n = m:formvalue("_newfwd.name")
local p = m:formvalue("_newfwd.proto")
local e = m:formvalue("_newfwd.extport")
local a = m:formvalue("_newfwd.intaddr")
local i = m:formvalue("_newfwd.intport")
if p == "other" or (p and a) then
created = TypedSection.create(self, section)
self.map:set(created, "target", "DNAT")
self.map:set(created, "src", "wan")
self.map:set(created, "dest", "lan")
self.map:set(created, "proto", (p ~= "other") and p or "all")
self.map:set(created, "src_dport", e)
self.map:set(created, "dest_ip", a)
self.map:set(created, "dest_port", i)
self.map:set(created, "_name", n)
end
if p ~= "other" then
created = nil
end
end
function s.parse(self, ...)
TypedSection.parse(self, ...)
if created then
m.uci:save("firewall")
luci.http.redirect(ds.build_url(
"admin", "network", "firewall", "redirect", created
))
end
end
function s.filter(self, sid)
return (self.map:get(sid, "target") ~= "SNAT")
end
name = s:option(DummyValue, "_name", translate("Name"))
function name.cfgvalue(self, s)
return self.map:get(s, "_name") or "-"
end
proto = s:option(DummyValue, "proto", translate("Protocol"))
proto.rawhtml = true
function proto.cfgvalue(self, s)
return ft.fmt_proto(self.map:get(s, "proto")) or "Any"
end
src = s:option(DummyValue, "src", translate("Source"))
src.rawhtml = true
src.width = "20%"
function src.cfgvalue(self, s)
local z = ft.fmt_zone(self.map:get(s, "src"))
local a = ft.fmt_ip(self.map:get(s, "src_ip"))
local p = ft.fmt_port(self.map:get(s, "src_port"))
local m = ft.fmt_mac(self.map:get(s, "src_mac"))
local s = "From %s in %s " %{
(a or "<var>any host</var>"),
(z or "<var>any zone</var>")
}
if p and m then
s = s .. "with source %s and %s" %{ p, m }
elseif p or m then
s = s .. "with source %s" %( p or m )
end
return s
end
via = s:option(DummyValue, "via", translate("Via"))
via.rawhtml = true
via.width = "20%"
function via.cfgvalue(self, s)
local a = ft.fmt_ip(self.map:get(s, "src_dip"))
local p = ft.fmt_port(self.map:get(s, "src_dport"))
--local z = self.map:get(s, "src")
--local s = "To %s " %(a or "<var>any %s IP</var>" %( z or "router" ))
return "To %s%s" %{
(a or "<var>any router IP</var>"),
(p and " at %s" % p or "")
}
end
dest = s:option(DummyValue, "dest", translate("Destination"))
dest.rawhtml = true
dest.width = "30%"
function dest.cfgvalue(self, s)
local z = ft.fmt_zone(self.map:get(s, "dest"))
local a = ft.fmt_ip(self.map:get(s, "dest_ip"))
local p = ft.fmt_port(self.map:get(s, "dest_port")) or
ft.fmt_port(self.map:get(s, "src_dport"))
return "Forward to %s%s in %s " %{
(a or "<var>any host</var>"),
(p and ", %s" % p or ""),
(z or "<var>any zone</var>")
}
end
return m

View file

@ -0,0 +1,300 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2010 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local sys = require "luci.sys"
local dsp = require "luci.dispatcher"
local nxo = require "nixio"
local nw = require "luci.model.network"
local m, s, o, k, v
arg[1] = arg[1] or ""
m = Map("firewall",
translate("Firewall - Traffic Rules"),
translate("This page allows you to change advanced properties of the \
traffic rule entry, such as matched source and destination \
hosts."))
m.redirect = dsp.build_url("admin/network/firewall/rules")
nw.init(m.uci)
local rule_type = m.uci:get("firewall", arg[1])
if rule_type == "redirect" and m:get(arg[1], "target") ~= "SNAT" then
rule_type = nil
end
if not rule_type then
luci.http.redirect(m.redirect)
return
--
-- SNAT
--
elseif rule_type == "redirect" then
local name = m:get(arg[1], "_name")
if not name or #name == 0 then
name = translate("(Unnamed SNAT)")
else
name = "SNAT %s" % name
end
m.title = "%s - %s" %{ translate("Firewall - Traffic Rules"), name }
local wan_zone = nil
m.uci:foreach("firewall", "zone",
function(s)
local n = s.network or s.name
if n then
local i
for i in n:gmatch("%S+") do
if i == "wan" then
wan_zone = s.name
return false
end
end
end
end)
s = m:section(NamedSection, arg[1], "redirect", "")
s.anonymous = true
s.addremove = false
o = s:option(Value, "_name", translate("Name"))
o.rmempty = true
o.size = 10
o = s:option(Value, "proto",
translate("Protocol"),
translate("You may specify multiple by selecting \"-- custom --\" and \
then entering protocols separated by space."))
o:value("all", "All protocols")
o:value("tcp udp", "TCP+UDP")
o:value("tcp", "TCP")
o:value("udp", "UDP")
o:value("icmp", "ICMP")
function o.cfgvalue(...)
local v = Value.cfgvalue(...)
if not v or v == "tcpudp" then
return "tcp udp"
end
return v
end
o = s:option(Value, "src", translate("Source zone"))
o.nocreate = true
o.default = "wan"
o.template = "cbi/firewall_zonelist"
o = s:option(DynamicList, "src_mac", translate("Source MAC address"))
o.rmempty = true
o.datatype = "neg(macaddr)"
o.placeholder = translate("any")
o = s:option(Value, "src_ip", translate("Source IP address"))
o.rmempty = true
o.datatype = "neg(ip4addr)"
o.placeholder = translate("any")
o = s:option(Value, "src_port",
translate("Source port"),
translate("Match incoming traffic originating from the given source \
port or port range on the client host."))
o.rmempty = true
o.datatype = "neg(portrange)"
o.placeholder = translate("any")
o = s:option(Value, "dest", translate("Destination zone"))
o.nocreate = true
o.default = "lan"
o.template = "cbi/firewall_zonelist"
o = s:option(Value, "dest_ip", translate("Destination IP address"))
o.datatype = "neg(ip4addr)"
for i, dataset in ipairs(luci.sys.net.arptable()) do
o:value(dataset["IP address"])
end
o = s:option(Value, "dest_port",
translate("Destination port"),
translate("Match forwarded traffic to the given destination port or \
port range."))
o.rmempty = true
o.placeholder = translate("any")
o.datatype = "portrange"
o = s:option(Value, "src_dip",
translate("SNAT IP address"),
translate("Rewrite matched traffic to the given address."))
o.rmempty = false
o.datatype = "ip4addr"
for k, v in ipairs(nw:get_interfaces()) do
local a
for k, a in ipairs(v:ipaddrs()) do
o:value(a:host():string(), '%s (%s)' %{
a:host():string(), v:shortname()
})
end
end
o = s:option(Value, "src_dport", translate("SNAT port"),
translate("Rewrite matched traffic to the given source port. May be \
left empty to only rewrite the IP address."))
o.datatype = "portrange"
o.rmempty = true
o.placeholder = translate('Do not rewrite')
--
-- Rule
--
else
s = m:section(NamedSection, arg[1], "rule", "")
s.anonymous = true
s.addremove = false
s:option(Value, "_name", translate("Name").." "..translate("(optional)"))
o = s:option(ListValue, "family", translate("Restrict to address family"))
o.rmempty = true
o:value("", translate("IPv4 and IPv6"))
o:value("ipv4", translate("IPv4 only"))
o:value("ipv6", translate("IPv6 only"))
o = s:option(Value, "proto", translate("Protocol"))
o:value("all", translate("Any"))
o:value("tcp udp", "TCP+UDP")
o:value("tcp", "TCP")
o:value("udp", "UDP")
o:value("icmp", "ICMP")
function o.cfgvalue(...)
local v = Value.cfgvalue(...)
if not v or v == "tcpudp" then
return "tcp udp"
end
return v
end
o = s:option(DynamicList, "icmp_type", translate("Match ICMP type"))
o:value("", "any")
o:value("echo-reply")
o:value("destination-unreachable")
o:value("network-unreachable")
o:value("host-unreachable")
o:value("protocol-unreachable")
o:value("port-unreachable")
o:value("fragmentation-needed")
o:value("source-route-failed")
o:value("network-unknown")
o:value("host-unknown")
o:value("network-prohibited")
o:value("host-prohibited")
o:value("TOS-network-unreachable")
o:value("TOS-host-unreachable")
o:value("communication-prohibited")
o:value("host-precedence-violation")
o:value("precedence-cutoff")
o:value("source-quench")
o:value("redirect")
o:value("network-redirect")
o:value("host-redirect")
o:value("TOS-network-redirect")
o:value("TOS-host-redirect")
o:value("echo-request")
o:value("router-advertisement")
o:value("router-solicitation")
o:value("time-exceeded")
o:value("ttl-zero-during-transit")
o:value("ttl-zero-during-reassembly")
o:value("parameter-problem")
o:value("ip-header-bad")
o:value("required-option-missing")
o:value("timestamp-request")
o:value("timestamp-reply")
o:value("address-mask-request")
o:value("address-mask-reply")
o = s:option(Value, "src", translate("Source zone"))
o.nocreate = true
o.allowany = true
o.default = "wan"
o.template = "cbi/firewall_zonelist"
o = s:option(Value, "src_mac", translate("Source MAC address"))
o.datatype = "list(macaddr)"
o.placeholder = translate("any")
o = s:option(Value, "src_ip", translate("Source address"))
o.datatype = "neg(ipaddr)"
o.placeholder = translate("any")
o = s:option(Value, "src_port", translate("Source port"))
o.datatype = "list(neg,portrange)"
o.placeholder = translate("any")
o = s:option(Value, "dest", translate("Destination zone"))
o.nocreate = true
o.allowany = true
o.allowlocal = true
o.template = "cbi/firewall_zonelist"
o = s:option(Value, "dest_ip", translate("Destination address"))
o.datatype = "neg(ipaddr)"
o.placeholder = translate("any")
o = s:option(Value, "dest_port", translate("Destination port"))
o.datatype = "list(neg,portrange)"
o.placeholder = translate("any")
o = s:option(ListValue, "target", translate("Action"))
o.default = "ACCEPT"
o:value("DROP", translate("drop"))
o:value("ACCEPT", translate("accept"))
o:value("REJECT", translate("reject"))
o:value("NOTRACK", translate("don't track"))
end
return m

View file

@ -0,0 +1,300 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local ds = require "luci.dispatcher"
local ft = require "luci.tools.firewall"
m = Map("firewall",
translate("Firewall - Traffic Rules"),
translate("Traffic rules define policies for packets traveling between \
different zones, for example to reject traffic between certain hosts \
or to open WAN ports on the router."))
--
-- Rules
--
s = m:section(TypedSection, "rule", translate("Traffic Rules"))
s.addremove = true
s.anonymous = true
s.sortable = true
s.template = "cbi/tblsection"
s.extedit = ds.build_url("admin/network/firewall/rules/%s")
s.defaults.target = "ACCEPT"
s.template_addremove = "firewall/cbi_addrule"
function s.create(self, section)
created = TypedSection.create(self, section)
end
function s.parse(self, ...)
TypedSection.parse(self, ...)
local i_n = m:formvalue("_newopen.name")
local i_p = m:formvalue("_newopen.proto")
local i_e = m:formvalue("_newopen.extport")
local i_x = m:formvalue("_newopen.submit")
local f_n = m:formvalue("_newfwd.name")
local f_s = m:formvalue("_newfwd.src")
local f_d = m:formvalue("_newfwd.dest")
local f_x = m:formvalue("_newfwd.submit")
if i_x then
created = TypedSection.create(self, section)
self.map:set(created, "target", "ACCEPT")
self.map:set(created, "src", "wan")
self.map:set(created, "proto", (i_p ~= "other") and i_p or "all")
self.map:set(created, "dest_port", i_e)
self.map:set(created, "_name", i_n)
if i_p ~= "other" and i_e and #i_e > 0 then
created = nil
end
elseif f_x then
created = TypedSection.create(self, section)
self.map:set(created, "target", "ACCEPT")
self.map:set(created, "src", f_s)
self.map:set(created, "dest", f_d)
self.map:set(created, "_name", f_n)
end
if created then
m.uci:save("firewall")
luci.http.redirect(ds.build_url(
"admin", "network", "firewall", "rules", created
))
end
end
name = s:option(DummyValue, "_name", translate("Name"))
function name.cfgvalue(self, s)
return self.map:get(s, "_name") or "-"
end
family = s:option(DummyValue, "family", translate("Family"))
function family.cfgvalue(self, s)
local f = self.map:get(s, "family")
if f and f:match("4") then
return translate("IPv4")
elseif f and f:match("6") then
return translate("IPv6")
else
return translate("Any")
end
end
proto = s:option(DummyValue, "proto", translate("Protocol"))
proto.rawhtml = true
proto.width = "20%"
function proto.cfgvalue(self, s)
return ft.fmt_proto(self.map:get(s, "proto"), self.map:get(s, "icmp_type"))
or "TCP+UDP"
end
src = s:option(DummyValue, "src", translate("Source"))
src.rawhtml = true
src.width = "20%"
function src.cfgvalue(self, s)
local z = ft.fmt_zone(self.map:get(s, "src"))
local a = ft.fmt_ip(self.map:get(s, "src_ip"))
local p = ft.fmt_port(self.map:get(s, "src_port"))
local m = ft.fmt_mac(self.map:get(s, "src_mac"))
local s = "From %s in %s " %{
(a or "<var>any host</var>"),
(z or "<var>any zone</var>")
}
if p and m then
s = s .. "with source %s and %s" %{ p, m }
elseif p or m then
s = s .. "with source %s" %( p or m )
end
return s
end
dest = s:option(DummyValue, "dest", translate("Destination"))
dest.rawhtml = true
dest.width = "20%"
function dest.cfgvalue(self, s)
local z = ft.fmt_zone(self.map:get(s, "dest"))
local a = ft.fmt_ip(self.map:get(s, "dest_ip"))
local p = ft.fmt_port(self.map:get(s, "dest_port"))
-- Forward
if z then
return "To %s%s in %s" %{
(a or "<var>any host</var>"),
(p and ", %s" % p or ""),
z
}
-- Input
else
return "To %s%s on <var>this device</var>" %{
(a or "<var>any router IP</var>"),
(p and " at %s" % p or "")
}
end
end
target = s:option(DummyValue, "target", translate("Action"))
target.rawhtml = true
target.width = "20%"
function target.cfgvalue(self, s)
local z = ft.fmt_zone(self.map:get(s, "dest"))
local l = ft.fmt_limit(self.map:get(s, "limit"), self.map:get(s, "limit_burst"))
local t = ft.fmt_target(self.map:get(s, "target"))
return "<var>%s</var> %s%s" %{
t,
(z and "forward" or "input"),
(l and " and limit to %s" % l or "")
}
end
--
-- SNAT
--
s = m:section(TypedSection, "redirect",
translate("Source NAT"),
translate("Source NAT is a specific form of masquerading which allows \
fine grained control over the source IP used for outgoing traffic, \
for example to map multiple WAN addresses to internal subnets."))
s.template = "cbi/tblsection"
s.addremove = true
s.anonymous = true
s.sortable = true
s.extedit = ds.build_url("admin/network/firewall/rules/%s")
s.template_addremove = "firewall/cbi_addsnat"
function s.create(self, section)
created = TypedSection.create(self, section)
end
function s.parse(self, ...)
TypedSection.parse(self, ...)
local n = m:formvalue("_newsnat.name")
local s = m:formvalue("_newsnat.src")
local d = m:formvalue("_newsnat.dest")
local a = m:formvalue("_newsnat.dip")
local p = m:formvalue("_newsnat.dport")
local x = m:formvalue("_newsnat.submit")
if x and a and #a > 0 then
created = TypedSection.create(self, section)
self.map:set(created, "target", "SNAT")
self.map:set(created, "src", s)
self.map:set(created, "dest", d)
self.map:set(created, "proto", "all")
self.map:set(created, "src_dip", a)
self.map:set(created, "src_dport", p)
self.map:set(created, "_name", n)
end
if created then
m.uci:save("firewall")
luci.http.redirect(ds.build_url(
"admin/network/firewall/rules", created
))
end
end
function s.filter(self, sid)
return (self.map:get(sid, "target") == "SNAT")
end
name = s:option(DummyValue, "_name", translate("Name"))
function name.cfgvalue(self, s)
return self.map:get(s, "_name") or "-"
end
proto = s:option(DummyValue, "proto", translate("Protocol"))
proto.rawhtml = true
function proto.cfgvalue(self, s)
return ft.fmt_proto(self.map:get(s, "proto")) or "TCP+UDP"
end
src = s:option(DummyValue, "src", translate("Source"))
src.rawhtml = true
src.width = "20%"
function src.cfgvalue(self, s)
local z = ft.fmt_zone(self.map:get(s, "src"))
local a = ft.fmt_ip(self.map:get(s, "src_ip"))
local p = ft.fmt_port(self.map:get(s, "src_port"))
local m = ft.fmt_mac(self.map:get(s, "src_mac"))
local s = "From %s in %s " %{
(a or "<var>any host</var>"),
(z or "<var>any zone</var>")
}
if p and m then
s = s .. "with source %s and %s" %{ p, m }
elseif p or m then
s = s .. "with source %s" %( p or m )
end
return s
end
dest = s:option(DummyValue, "dest", translate("Destination"))
dest.rawhtml = true
dest.width = "30%"
function dest.cfgvalue(self, s)
local z = ft.fmt_zone(self.map:get(s, "dest"))
local a = ft.fmt_ip(self.map:get(s, "dest_ip"))
local p = ft.fmt_port(self.map:get(s, "dest_port")) or
ft.fmt_port(self.map:get(s, "src_dport"))
return "To %s%s in %s " %{
(a or "<var>any host</var>"),
(p and ", %s" % p or ""),
(z or "<var>any zone</var>")
}
end
snat = s:option(DummyValue, "via", translate("SNAT"))
snat.rawhtml = true
snat.width = "20%"
function snat.cfgvalue(self, s)
local a = ft.fmt_ip(self.map:get(s, "src_dip"))
local p = ft.fmt_port(self.map:get(s, "src_dport"))
--local z = self.map:get(s, "src")
--local s = "To %s " %(a or "<var>any %s IP</var>" %( z or "router" ))
if a and p then
return "Rewrite to source %s, %s" %{ a, p }
elseif a or p then
return "Rewrite to source %s" %( a or p )
else
return "Bug"
end
end
return m

View file

@ -14,15 +14,16 @@ $Id$
local nw = require "luci.model.network"
local fw = require "luci.model.firewall"
local utl = require "luci.util"
local dsp = require "luci.dispatcher"
local ds = require "luci.dispatcher"
local ut = require "luci.util"
local m, p, i, v
local s, name, net, family, msrc, mdest, log, lim
local s2, out, inp
local has_v2 = nixio.fs.access("/lib/firewall/fw.sh")
local out, inp
require("luci.tools.webadmin")
m = Map("firewall", translate("Firewall - Zone Settings"))
m.redirect = luci.dispatcher.build_url("admin/network/firewall")
m.redirect = luci.dispatcher.build_url("admin/network/firewall/zones")
fw.init(m.uci)
nw.init(m.uci)
@ -30,20 +31,25 @@ nw.init(m.uci)
local zone = fw:get_zone(arg[1])
if not zone then
luci.http.redirect(dsp.build_url("admin", "network", "firewall"))
luci.http.redirect(dsp.build_url("admin/network/firewall/zones"))
return
else
m.title = "%s - %s" %{
translate("Firewall - Zone Settings"),
translatef("Zone %q", zone:name() or "?")
}
end
s = m:section(NamedSection, zone.sid, "zone",
translatef("Zone %q", zone:name()),
translatef("This section defines common properties of %q. " ..
"The <em>input</em> and <em>output</em> options set the default "..
"policies for traffic entering and leaving this zone while the " ..
"<em>forward</em> option describes the policy for forwarded traffic " ..
"between different networks within the zone. " ..
"<em>Covered networks</em> specifies which available networks are " ..
"member of this zone.", zone:name()))
translatef("This section defines common properties of %q. \
The <em>input</em> and <em>output</em> options set the default \
policies for traffic entering and leaving this zone while the \
<em>forward</em> option describes the policy for forwarded traffic \
between different networks within the zone. \
<em>Covered networks</em> specifies which available networks are \
member of this zone.", zone:name()))
s.anonymous = true
s.addremove = false
@ -73,15 +79,18 @@ function name.write(self, section, value)
inp.exclude = value
end
m.redirect = luci.dispatcher.build_url(
"admin", "network", "firewall", "zones", value
)
m.redirect = ds.build_url("admin/network/firewall/zones", value)
m.title = "%s - %s" %{
translate("Firewall - Zone Settings"),
translatef("Zone %q", value or "?")
}
end
p = {}
p[1] = s:taboption("general", ListValue, "input", translate("Input"))
p[2] = s:taboption("general", ListValue, "output", translate("Output"))
p[3] = s:taboption("general", ListValue, "forward", translate("Forward"))
p = {
s:taboption("general", ListValue, "input", translate("Input")),
s:taboption("general", ListValue, "output", translate("Output")),
s:taboption("general", ListValue, "forward", translate("Forward"))
}
for i, v in ipairs(p) do
v:value("REJECT", translate("reject"))
@ -109,27 +118,25 @@ function net.write(self, section, value)
zone:clear_networks()
local n
for n in utl.imatch(value) do
for n in ut.imatch(value) do
zone:add_network(n)
end
end
if has_v2 then
family = s:taboption("advanced", ListValue, "family",
family = s:taboption("advanced", ListValue, "family",
translate("Restrict to address family"))
family.rmempty = true
family:value("", translate("IPv4 and IPv6"))
family:value("ipv4", translate("IPv4 only"))
family:value("ipv6", translate("IPv6 only"))
end
family.rmempty = true
family:value("", translate("IPv4 and IPv6"))
family:value("ipv4", translate("IPv4 only"))
family:value("ipv6", translate("IPv6 only"))
msrc = s:taboption("advanced", DynamicList, "masq_src",
translate("Restrict Masquerading to given source subnets"))
msrc.optional = true
msrc.datatype = "neg(network)"
msrc.datatype = "neg_network_ip4addr"
msrc.placeholder = "0.0.0.0/0"
msrc:depends("family", "")
msrc:depends("family", "ipv4")
@ -138,7 +145,7 @@ mdest = s:taboption("advanced", DynamicList, "masq_dest",
translate("Restrict Masquerading to given destination subnets"))
mdest.optional = true
mdest.datatype = "neg(network)"
mdest.datatype = "neg_network_ip4addr"
mdest.placeholder = "0.0.0.0/0"
mdest:depends("family", "")
mdest:depends("family", "ipv4")
@ -146,30 +153,28 @@ mdest:depends("family", "ipv4")
s:taboption("advanced", Flag, "conntrack",
translate("Force connection tracking"))
if has_v2 then
log = s:taboption("advanced", Flag, "log",
log = s:taboption("advanced", Flag, "log",
translate("Enable logging on this zone"))
log.rmempty = true
log.enabled = "1"
log.rmempty = true
log.enabled = "1"
lim = s:taboption("advanced", Value, "log_limit",
lim = s:taboption("advanced", Value, "log_limit",
translate("Limit log messages"))
lim.placeholder = "10/minute"
lim:depends("log", "1")
end
lim.placeholder = "10/minute"
lim:depends("log", "1")
s2 = m:section(NamedSection, zone.sid, "fwd_out",
translate("Inter-Zone Forwarding"),
translatef("The options below control the forwarding policies between " ..
"this zone (%s) and other zones. <em>Destination zones</em> cover " ..
"forwarded traffic <strong>originating from %q</strong>. " ..
"<em>Source zones</em> match forwarded traffic from other zones " ..
"<strong>targeted at %q</strong>. The forwarding rule is " ..
"<em>unidirectional</em>, e.g. a forward from lan to wan does " ..
"<em>not</em> imply a permission to forward from wan to lan as well.",
translatef("The options below control the forwarding policies between \
this zone (%s) and other zones. <em>Destination zones</em> cover \
forwarded traffic <strong>originating from %q</strong>. \
<em>Source zones</em> match forwarded traffic from other zones \
<strong>targeted at %q</strong>. The forwarding rule is \
<em>unidirectional</em>, e.g. a forward from lan to wan does \
<em>not</em> imply a permission to forward from wan to lan as well.",
zone:name(), zone:name(), zone:name()
))
@ -220,7 +225,7 @@ function out.write(self, section, value)
zone:del_forwardings_by("src")
local f
for f in utl.imatch(value) do
for f in ut.imatch(value) do
zone:add_forwarding_to(f)
end
end
@ -229,7 +234,7 @@ function inp.write(self, section, value)
zone:del_forwardings_by("dest")
local f
for f in utl.imatch(value) do
for f in ut.imatch(value) do
zone:add_forwarding_from(f)
end
end

View file

@ -0,0 +1,88 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local ds = require "luci.dispatcher"
local fw = require "luci.model.firewall"
local m, s, o, p, i, v
m = Map("firewall",
translate("Firewall - Zone Settings"),
translate("The firewall creates zones over your network interfaces to control network traffic flow."))
fw.init(m.uci)
s = m:section(TypedSection, "defaults", translate("General Settings"))
s.anonymous = true
s.addremove = false
s:option(Flag, "syn_flood", translate("Enable SYN-flood protection"))
o = s:option(Flag, "drop_invalid", translate("Drop invalid packets"))
o.default = o.disabled
p = {
s:option(ListValue, "input", translate("Input")),
s:option(ListValue, "output", translate("Output")),
s:option(ListValue, "forward", translate("Forward"))
}
for i, v in ipairs(p) do
v:value("REJECT", translate("reject"))
v:value("DROP", translate("drop"))
v:value("ACCEPT", translate("accept"))
end
s = m:section(TypedSection, "zone", translate("Zones"))
s.template = "cbi/tblsection"
s.anonymous = true
s.addremove = true
s.extedit = ds.build_url("admin", "network", "firewall", "zones", "%s")
function s.create(self)
local z = fw:new_zone()
if z then
luci.http.redirect(
ds.build_url("admin", "network", "firewall", "zones", z.sid)
)
end
end
function s.remove(self, section)
return fw:del_zone(section)
end
o = s:option(DummyValue, "_info", translate("Zone ⇒ Forwardings"))
o.template = "cbi/firewall_zoneforwards"
o.cfgvalue = function(self, section)
return self.map:get(section, "name")
end
p = {
s:option(ListValue, "input", translate("Input")),
s:option(ListValue, "output", translate("Output")),
s:option(ListValue, "forward", translate("Forward"))
}
for i, v in ipairs(p) do
v:value("REJECT", translate("reject"))
v:value("DROP", translate("drop"))
v:value("ACCEPT", translate("accept"))
end
s:option(Flag, "masq", translate("Masquerading"))
s:option(Flag, "mtu_fix", translate("MSS clamping"))
return m

View file

@ -1,48 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
require("luci.sys")
m = Map("firewall", translate("Port forwarding"),
translate("Port forwarding allows to provide network services in " ..
"the internal network to an external network."))
s = m:section(TypedSection, "redirect", "")
s:depends("src", "wan")
s.defaults.src = "wan"
s.template = "cbi/tblsection"
s.addremove = true
s.anonymous = true
name = s:option(Value, "_name", translate("Name"), translate("(optional)"))
name.size = 10
proto = s:option(ListValue, "proto", translate("Protocol"))
proto:value("tcp", "TCP")
proto:value("udp", "UDP")
proto:value("tcpudp", "TCP+UDP")
dport = s:option(Value, "src_dport", translate("External port"))
dport.size = 5
to = s:option(Value, "dest_ip", translate("Internal IP address"))
for i, dataset in ipairs(luci.sys.net.arptable()) do
to:value(dataset["IP address"])
end
toport = s:option(Value, "dest_port", translate("Internal port"),
translate("(optional)"))
toport.size = 5
return m

View file

@ -1,155 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 Steven Barth <steven@midlink.org>
Copyright 2010 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local has_v2 = nixio.fs.access("/lib/firewall/fw.sh")
local dsp = require "luci.dispatcher"
arg[1] = arg[1] or ""
m = Map("firewall", translate("Advanced Rules"),
translate("Advanced rules let you customize the firewall to your " ..
"needs. Only new connections will be matched. Packets " ..
"belonging to already open connections are automatically " ..
"allowed to pass the firewall."))
m.redirect = dsp.build_url("admin", "network", "firewall")
if not m.uci:get(arg[1]) == "rule" then
luci.http.redirect(m.redirect)
return
end
s = m:section(NamedSection, arg[1], "rule", "")
s.anonymous = true
s.addremove = false
s:tab("general", translate("General Settings"))
s:tab("advanced", translate("Advanced Options"))
back = s:option(DummyValue, "_overview", translate("Overview"))
back.value = ""
back.titleref = dsp.build_url("admin", "network", "firewall", "rule")
name = s:taboption("general", Value, "_name", translate("Name").." "..translate("(optional)"))
name.rmempty = true
src = s:taboption("general", Value, "src", translate("Source zone"))
src.nocreate = true
src.default = "wan"
src.template = "cbi/firewall_zonelist"
dest = s:taboption("advanced", Value, "dest", translate("Destination zone"))
dest.nocreate = true
dest.allowlocal = true
dest.template = "cbi/firewall_zonelist"
proto = s:taboption("general", Value, "proto", translate("Protocol"))
proto.optional = true
proto:value("all", translate("Any"))
proto:value("tcpudp", "TCP+UDP")
proto:value("tcp", "TCP")
proto:value("udp", "UDP")
proto:value("icmp", "ICMP")
icmpt = s:taboption("general", Value, "icmp_type", translate("Match ICMP type"))
icmpt:depends("proto", "icmp")
icmpt:value("", "any")
icmpt:value("echo-reply")
icmpt:value("destination-unreachable")
icmpt:value("network-unreachable")
icmpt:value("host-unreachable")
icmpt:value("protocol-unreachable")
icmpt:value("port-unreachable")
icmpt:value("fragmentation-needed")
icmpt:value("source-route-failed")
icmpt:value("network-unknown")
icmpt:value("host-unknown")
icmpt:value("network-prohibited")
icmpt:value("host-prohibited")
icmpt:value("TOS-network-unreachable")
icmpt:value("TOS-host-unreachable")
icmpt:value("communication-prohibited")
icmpt:value("host-precedence-violation")
icmpt:value("precedence-cutoff")
icmpt:value("source-quench")
icmpt:value("redirect")
icmpt:value("network-redirect")
icmpt:value("host-redirect")
icmpt:value("TOS-network-redirect")
icmpt:value("TOS-host-redirect")
icmpt:value("echo-request")
icmpt:value("router-advertisement")
icmpt:value("router-solicitation")
icmpt:value("time-exceeded")
icmpt:value("ttl-zero-during-transit")
icmpt:value("ttl-zero-during-reassembly")
icmpt:value("parameter-problem")
icmpt:value("ip-header-bad")
icmpt:value("required-option-missing")
icmpt:value("timestamp-request")
icmpt:value("timestamp-reply")
icmpt:value("address-mask-request")
icmpt:value("address-mask-reply")
src_ip = s:taboption("general", Value, "src_ip", translate("Source address"))
src_ip.optional = true
src_ip.datatype = has_v2 and "neg_ipaddr" or "neg_ip4addr"
src_ip.placeholder = translate("any")
sport = s:taboption("general", Value, "src_port", translate("Source port"))
sport.optional = true
sport.datatype = "portrange"
sport.placeholder = "0-65535"
sport:depends("proto", "tcp")
sport:depends("proto", "udp")
sport:depends("proto", "tcpudp")
dest_ip = s:taboption("general", Value, "dest_ip", translate("Destination address"))
dest_ip.optional = true
dest_ip.datatype = has_v2 and "neg_ipaddr" or "neg_ip4addr"
dest_ip.placeholder = translate("any")
dport = s:taboption("general", Value, "dest_port", translate("Destination port"))
dport.optional = true
dport.datatype = "portrange"
dport:depends("proto", "tcp")
dport:depends("proto", "udp")
dport:depends("proto", "tcpudp")
dport.placeholder = "0-65535"
jump = s:taboption("general", ListValue, "target", translate("Action"))
jump.rmempty = true
jump.default = "ACCEPT"
jump:value("DROP", translate("drop"))
jump:value("ACCEPT", translate("accept"))
jump:value("REJECT", translate("reject"))
jump:value("NOTRACK", translate("don't track"))
smac = s:taboption("advanced", Value, "src_mac", translate("Source MAC address"))
smac.optional = true
smac.datatype = "macaddr"
smac.placeholder = translate("any")
if has_v2 then
family = s:taboption("advanced", ListValue, "family", translate("Restrict to address family"))
family.rmempty = true
family:value("", translate("IPv4 and IPv6"))
family:value("ipv4", translate("IPv4 only"))
family:value("ipv6", translate("IPv6 only"))
end
return m

View file

@ -1,273 +0,0 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2008 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
local nw = require "luci.model.network"
local fw = require "luci.model.firewall"
local ds = require "luci.dispatcher"
local has_v2 = nixio.fs.access("/lib/firewall/fw.sh")
require("luci.tools.webadmin")
m = Map("firewall", translate("Firewall"), translate("The firewall creates zones over your network interfaces to control network traffic flow."))
fw.init(m.uci)
nw.init(m.uci)
s = m:section(TypedSection, "defaults")
s.anonymous = true
s.addremove = false
s:tab("general", translate("General Settings"))
s:tab("custom", translate("Custom Rules"))
s:taboption("general", Flag, "syn_flood", translate("Enable SYN-flood protection"))
local di = s:taboption("general", Flag, "drop_invalid", translate("Drop invalid packets"))
di.rmempty = false
function di.cfgvalue(...)
return AbstractValue.cfgvalue(...) or "1"
end
p = {}
p[1] = s:taboption("general", ListValue, "input", translate("Input"))
p[2] = s:taboption("general", ListValue, "output", translate("Output"))
p[3] = s:taboption("general", ListValue, "forward", translate("Forward"))
for i, v in ipairs(p) do
v:value("REJECT", translate("reject"))
v:value("DROP", translate("drop"))
v:value("ACCEPT", translate("accept"))
end
custom = s:taboption("custom", Value, "_custom",
translate("Custom Rules (/etc/firewall.user)"))
custom.template = "cbi/tvalue"
custom.rows = 20
function custom.cfgvalue(self, section)
return nixio.fs.readfile("/etc/firewall.user")
end
function custom.write(self, section, value)
value = value:gsub("\r\n?", "\n")
nixio.fs.writefile("/etc/firewall.user", value)
end
s = m:section(TypedSection, "zone", translate("Zones"))
s.template = "cbi/tblsection"
s.anonymous = true
s.addremove = true
s.extedit = ds.build_url("admin", "network", "firewall", "zones", "%s")
function s.create(self)
local z = fw:new_zone()
if z then
luci.http.redirect(
ds.build_url("admin", "network", "firewall", "zones", z.sid)
)
end
end
function s.remove(self, section)
return fw:del_zone(section)
end
info = s:option(DummyValue, "_info", translate("Zone ⇒ Forwardings"))
info.template = "cbi/firewall_zoneforwards"
function info.cfgvalue(self, section)
return self.map:get(section, "name")
end
p = {}
p[1] = s:option(ListValue, "input", translate("Input"))
p[2] = s:option(ListValue, "output", translate("Output"))
p[3] = s:option(ListValue, "forward", translate("Forward"))
for i, v in ipairs(p) do
v:value("REJECT", translate("reject"))
v:value("DROP", translate("drop"))
v:value("ACCEPT", translate("accept"))
end
s:option(Flag, "masq", translate("Masquerading"))
s:option(Flag, "mtu_fix", translate("MSS clamping"))
local created = nil
--
-- Redirects
--
s = m:section(TypedSection, "redirect", translate("Redirections"))
s.template = "cbi/tblsection"
s.addremove = true
s.anonymous = true
s.sortable = true
s.extedit = ds.build_url("admin", "network", "firewall", "redirect", "%s")
function s.create(self, section)
created = TypedSection.create(self, section)
end
function s.parse(self, ...)
TypedSection.parse(self, ...)
if created then
m.uci:save("firewall")
luci.http.redirect(ds.build_url(
"admin", "network", "firewall", "redirect", created
))
end
end
name = s:option(DummyValue, "_name", translate("Name"))
function name.cfgvalue(self, s)
return self.map:get(s, "_name") or "-"
end
proto = s:option(DummyValue, "proto", translate("Protocol"))
function proto.cfgvalue(self, s)
local p = self.map:get(s, "proto")
if not p or p == "tcpudp" then
return "TCP+UDP"
else
return p:upper()
end
end
src = s:option(DummyValue, "src", translate("Source"))
function src.cfgvalue(self, s)
local rv = "%s:%s:%s" % {
self.map:get(s, "src") or "*",
self.map:get(s, "src_ip") or "0.0.0.0/0",
self.map:get(s, "src_port") or "*"
}
local mac = self.map:get(s, "src_mac")
if mac then
rv = rv .. ", MAC " .. mac
end
return rv
end
via = s:option(DummyValue, "via", translate("Via"))
function via.cfgvalue(self, s)
return "%s:%s:%s" % {
translate("Device"),
self.map:get(s, "src_dip") or "0.0.0.0/0",
self.map:get(s, "src_dport") or "*"
}
end
dest = s:option(DummyValue, "dest", translate("Destination"))
function dest.cfgvalue(self, s)
return "%s:%s:%s" % {
self.map:get(s, "dest") or "*",
self.map:get(s, "dest_ip") or "0.0.0.0/0",
self.map:get(s, "dest_port") or "*"
}
end
target = s:option(DummyValue, "target", translate("Action"))
function target.cfgvalue(self, s)
return self.map:get(s, "target") or "DNAT"
end
--
-- Rules
--
s = m:section(TypedSection, "rule", translate("Rules"))
s.addremove = true
s.anonymous = true
s.sortable = true
s.template = "cbi/tblsection"
s.extedit = ds.build_url("admin", "network", "firewall", "rule", "%s")
s.defaults.target = "ACCEPT"
function s.create(self, section)
local created = TypedSection.create(self, section)
m.uci:save("firewall")
luci.http.redirect(ds.build_url(
"admin", "network", "firewall", "rule", created
))
return
end
name = s:option(DummyValue, "_name", translate("Name"))
function name.cfgvalue(self, s)
return self.map:get(s, "_name") or "-"
end
if has_v2 then
family = s:option(DummyValue, "family", translate("Family"))
function family.cfgvalue(self, s)
local f = self.map:get(s, "family")
if f and f:match("4") then
return translate("IPv4 only")
elseif f and f:match("6") then
return translate("IPv6 only")
else
return translate("IPv4 and IPv6")
end
end
end
proto = s:option(DummyValue, "proto", translate("Protocol"))
function proto.cfgvalue(self, s)
local p = self.map:get(s, "proto")
local t = self.map:get(s, "icmp_type")
if p == "icmp" and t then
return "ICMP (%s)" % t
elseif p == "tcpudp" or not p then
return "TCP+UDP"
else
return p:upper()
end
end
src = s:option(DummyValue, "src", translate("Source"))
function src.cfgvalue(self, s)
local rv = "%s:%s:%s" % {
self.map:get(s, "src") or "*",
self.map:get(s, "src_ip") or "0.0.0.0/0",
self.map:get(s, "src_port") or "*"
}
local mac = self.map:get(s, "src_mac")
if mac then
rv = rv .. ", MAC " .. mac
end
return rv
end
dest = s:option(DummyValue, "dest", translate("Destination"))
function dest.cfgvalue(self, s)
return "%s:%s:%s" % {
self.map:get(s, "dest") or translate("Device"),
self.map:get(s, "dest_ip") or "0.0.0.0/0",
self.map:get(s, "dest_port") or "*"
}
end
s:option(DummyValue, "target", translate("Action"))
return m

View file

@ -0,0 +1,213 @@
--[[
LuCI - Lua Configuration Interface
Copyright 2011 Jo-Philipp Wich <xm@subsignal.org>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
$Id$
]]--
module("luci.tools.firewall", package.seeall)
local ut = require "luci.util"
local ip = require "luci.ip"
local nx = require "nixio"
local tr, trf = luci.i18n.translate, luci.i18n.translatef
function fmt_neg(x)
if type(x) == "string" then
local v, neg = x:gsub("^ *! *", "")
if neg > 0 then
return v, "%s " % tr("not")
else
return x, ""
end
end
return x, ""
end
function fmt_mac(x)
if x and #x > 0 then
local m, n
local l = { tr("MAC"), " " }
for m in ut.imatch(x) do
m, n = fmt_neg(m)
l[#l+1] = "<var>%s%s</var>" %{ n, m }
l[#l+1] = ", "
end
if #l > 1 then
l[#l] = nil
if #l > 3 then
l[1] = tr("MACs")
end
return table.concat(l, "")
end
end
end
function fmt_port(x)
if x and #x > 0 then
local p, n
local l = { tr("port"), " " }
for p in ut.imatch(x) do
p, n = fmt_neg(p)
local a, b = p:match("(%d+)%D+(%d+)")
if a and b then
l[1] = tr("ports")
l[#l+1] = "<var>%s%d-%d</var>" %{ n, a, b }
else
l[#l+1] = "<var>%s%d</var>" %{ n, p }
end
l[#l+1] = ", "
end
if #l > 1 then
l[#l] = nil
if #l > 3 then
l[1] = tr("ports")
end
return table.concat(l, "")
end
end
end
function fmt_ip(x)
if x and #x > 0 then
local l = { tr("IP"), " " }
local v, a, n
for v in ut.imatch(x) do
v, n = fmt_neg(v)
a, m = v:match("(%S+)/(%d+%.%S+)")
a = a or v
a = a:match(":") and ip.IPv6(a, m) or ip.IPv4(a, m)
if a and (a:is6() or a:prefix() < 32) then
l[1] = tr("IP range")
l[#l+1] = "<var title='%s - %s'>%s%s</var>" %{
a:minhost():string(),
a:maxhost():string(),
n, a:string()
}
else
l[#l+1] = "<var>%s%s</var>" %{
n,
a and a:string() or v
}
end
l[#l+1] = ", "
end
if #l > 1 then
l[#l] = nil
if #l > 3 then
l[1] = tr("IPs")
end
return table.concat(l, "")
end
end
end
function fmt_zone(x)
if x == "*" then
return "<var>%s</var>" % tr("any zone")
elseif x and #x > 0 then
return "<var>%s</var>" % x
end
end
function fmt_icmp_type(x)
if x and #x > 0 then
local t, v, n
local l = { tr("type"), " " }
for v in ut.imatch(x) do
v, n = fmt_neg(v)
l[#l+1] = "<var>%s%s</var>" %{ n, v }
l[#l+1] = ", "
end
if #l > 1 then
l[#l] = nil
if #l > 3 then
l[1] = tr("types")
end
return table.concat(l, "")
end
end
end
function fmt_proto(x, icmp_types)
if x and #x > 0 then
local v, n
local l = { }
local t = fmt_icmp_type(icmp_types)
for v in ut.imatch(x) do
v, n = fmt_neg(v)
if v == "tcpudp" then
l[#l+1] = "TCP"
l[#l+1] = "UDP"
l[#l+1] = ", "
elseif v ~= "all" then
local p = nx.getproto(v)
if p then
-- ICMP
if (p.proto == 1 or p.proto == 58) and t then
l[#l+1] = trf(
"%s%s with %s",
n, p.aliases[1] or p.name, t
)
else
l[#l+1] = "%s%s" %{
n,
p.aliases[1] or p.name
}
end
l[#l+1] = ", "
end
end
end
if #l > 0 then
l[#l] = nil
return table.concat(l, "")
end
end
end
function fmt_limit(limit, burst)
burst = tonumber(burst)
if limit and #limit > 0 then
local l, u = limit:match("(%d+)/(%w+)")
l = tonumber(l or limit)
u = u or "second"
if l then
if u:match("^s") then
u = tr("second")
elseif u:match("^m") then
u = tr("minute")
elseif u:match("^h") then
u = tr("hour")
elseif u:match("^d") then
u = tr("day")
end
if burst and burst > 0 then
return trf("<var>%d</var> pkts. per <var>%s</var>, \
burst <var>%d</var> pkts.", l, u, burst)
else
return trf("<var>%d</var> pkts. per <var>%s</var>", l, u)
end
end
end
end
function fmt_target(x)
if x == "ACCEPT" then
return tr("Accept")
elseif x == "REJECT" then
return tr("Refuse")
elseif x == "NOTRACK" then
return tr("Do not track")
else --if x == "DROP" then
return tr("Discard")
end
end

View file

@ -0,0 +1,90 @@
<div class="cbi-section-create cbi-tblsection-create">
<br />
<table class="cbi-section-table" style="width:700px; margin-left:5px">
<tr class="cbi-section-table-titles">
<th class="cbi-section-table-cell" colspan="6"><%:New port forward%>:</th>
</tr>
<tr class="cbi-section-table-descr">
<th class="cbi-section-table-cell"><%:Name%></th>
<th class="cbi-section-table-cell"><%:Protocol%></th>
<th class="cbi-section-table-cell"><%:External port%></th>
<th class="cbi-section-table-cell"><%:Internal IP address%></th>
<th class="cbi-section-table-cell"><%:Internal port%></th>
<th class="cbi-section-table-cell"></th>
</tr>
<tr class="cbi-section-table-row">
<td class="cbi-section-table-cell">
<input type="text" class="cbi-input-text" id="_newfwd.name" name="_newfwd.name" placeholder="<%:New port forward%>" />
</td>
<td class="cbi-section-table-cell" style="width:110px">
<select class="cbi-input-select" id="_newfwd.proto" name="_newfwd.proto">
<option value="tcp udp">TCP+UDP</option>
<option value="tcp">TCP</option>
<option value="udp">UDP</option>
<option value="other"><%:Other...%></option>
</select>
</td>
<td class="cbi-section-table-cell" style="width:110px">
<input type="text" class="cbi-input-text" id="_newfwd.extport" name="_newfwd.extport" />
</td>
<td class="cbi-section-table-cell" style="width:110px">
<input type="text" class="cbi-input-text" id="_newfwd.intaddr" name="_newfwd.intaddr" />
</td>
<td class="cbi-section-table-cell" style="width:110px">
<input type="text" class="cbi-input-text" id="_newfwd.intport" name="_newfwd.intport" />
</td>
<td class="cbi-section-table-cell">
<input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" value="<%:Add%>" />
</td>
</tr>
</table>
<script type="text/javascript">//<![CDATA[
cbi_validate_field('_newfwd.extport', true, 'portrange');
cbi_validate_field('_newfwd.intaddr', true, 'host');
cbi_validate_field('_newfwd.intport', true, 'portrange');
cbi_combobox_init('_newfwd.intaddr', {
<% local i, e; for i, e in ipairs(luci.sys.net.arptable()) do -%>
<%- if i > 1 then %>,<% end -%>'<%=e["IP address"]%>': '<%=e["IP address"]%>'
<%- end %> }, '', '<%: -- custom -- %>');
cbi_bind(document.getElementById('_newfwd.extport'), 'blur',
function() {
var n = document.getElementById('_newfwd.name');
var p = document.getElementById('_newfwd.proto');
var i = document.getElementById('_newfwd.intport');
var hints = {
/* port name 0=both, 1=tcp, 2=udp, 3=other */
21: [ 'FTP', 1 ],
22: [ 'SSH', 1 ],
53: [ 'DNS', 0 ],
80: [ 'HTTP', 1 ],
443: [ 'HTTPS', 1 ],
3389: [ 'RDP', 1 ],
5900: [ 'VNC', 1 ],
};
if (!this.className.match(/invalid/))
{
if (!i.value) i.value = this.value;
var hint = hints[this.value || 0] || hints[i.value || 0];
if (hint)
{
p.selectedIndex = hint[1];
if (!n.value)
n.value = hint[0];
}
else if (!n.value)
{
n.value = 'Forward' + this.value;
}
}
});
cbi_validate_field('cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>', true, 'uciname');
//]]></script>
</div>

View file

@ -0,0 +1,112 @@
<%
local fw = require "luci.model.firewall".init()
local wz = fw:get_zone("wan")
local lz = fw:get_zone("lan")
%>
<div class="cbi-section-create cbi-tblsection-create">
<% if wz and lz then %>
<br />
<table class="cbi-section-table" style="margin-left:5px">
<tr class="cbi-section-table-titles">
<th class="cbi-section-table-cell left" colspan="4"><%:Open ports on router%>:</th>
</tr>
<tr class="cbi-section-table-descr">
<th class="cbi-section-table-cell"><%:Name%></th>
<th class="cbi-section-table-cell"><%:Protocol%></th>
<th class="cbi-section-table-cell"><%:External port%></th>
<th class="cbi-section-table-cell"></th>
</tr>
<tr class="cbi-section-table-row">
<td class="cbi-section-table-cell" style="width:130px">
<input type="text" class="cbi-input-text" id="_newopen.name" name="_newopen.name" placeholder="<%:New input rule%>" />
</td>
<td class="cbi-section-table-cell" style="width:110px">
<select class="cbi-input-select" id="_newopen.proto" name="_newopen.proto">
<option value="tcp udp">TCP+UDP</option>
<option value="tcp">TCP</option>
<option value="udp">UDP</option>
<option value="other"><%:Other...%></option>
</select>
</td>
<td class="cbi-section-table-cell" style="width:110px">
<input type="text" class="cbi-input-text" id="_newopen.extport" name="_newopen.extport" />
</td>
<td class="cbi-section-table-cell left">
<input type="submit" class="cbi-button cbi-button-add" name="_newopen.submit" value="<%:Add%>" />
</td>
</tr>
</table>
<table class="cbi-section-table" style="margin-left:5px">
<tr class="cbi-section-table-titles">
<th class="cbi-section-table-cell left" colspan="6"><br /><%:New forward rule%>:</th>
</tr>
<tr class="cbi-section-table-descr">
<th class="cbi-section-table-cell"><%:Name%></th>
<th class="cbi-section-table-cell"><%:Source zone%></th>
<th class="cbi-section-table-cell"><%:Destination zone%></th>
<th class="cbi-section-table-cell"></th>
</tr>
<tr class="cbi-section-table-row">
<td class="cbi-section-table-cell" style="width:130px">
<input type="text" class="cbi-input-text" id="_newfwd.name" name="_newfwd.name" placeholder="<%:New forward rule%>" />
</td>
<td class="cbi-section-table-cell" style="width:110px">
<select class="cbi-input-text" id="_newfwd.src" name="_newfwd.src">
<% local k, v; for k, v in ipairs(fw:get_zones()) do -%>
<option<%=ifattr(v:name() == "lan", "selected", "selected")%> value="<%=v:name()%>"><%=v:name()%></option>
<%- end %>
</select>
</td>
<td class="cbi-section-table-cell" style="width:110px">
<select class="cbi-input-text" id="_newfwd.dest" name="_newfwd.dest">
<% local k, v; for k, v in ipairs(fw:get_zones()) do -%>
<option<%=ifattr(v:name() == "wan", "selected", "selected")%> value="<%=v:name()%>"><%=v:name()%></option>
<%- end %>
</select>
</td>
<td class="cbi-section-table-cell left">
<input type="submit" class="cbi-button cbi-button-link" name="_newfwd.submit" value="<%:Add and edit...%>" />
</td>
</tr>
</table>
<script type="text/javascript">//<![CDATA[
cbi_validate_field('_newopen.extport', true, 'list(portrange)');
cbi_bind(document.getElementById('_newopen.extport'), 'blur',
function() {
var n = document.getElementById('_newopen.name');
var p = document.getElementById('_newopen.proto');
var hints = {
/* port name 0=both, 1=tcp, 2=udp, 3=other */
22: [ 'SSH', 1 ],
53: [ 'DNS', 0 ],
80: [ 'HTTP', 1 ],
443: [ 'HTTPS', 1 ],
};
if (!this.className.match(/invalid/))
{
var hint = hints[this.value || 0];
if (hint)
{
p.selectedIndex = hint[1];
if (!n.value)
n.value = hint[0];
}
else if (!n.value && this.value)
{
n.value = 'Open' + this.value;
}
}
});
cbi_validate_field('cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>', true, 'uciname');
//]]></script>
<% else %>
<input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" value="<%:Add%>" />
<% end %>
</div>

View file

@ -0,0 +1,65 @@
<%
local fw = require "luci.model.firewall".init()
local nw = require "luci.model.network".init()
local wz = fw:get_zone("wan")
local lz = fw:get_zone("lan")
%>
<div class="cbi-section-create cbi-tblsection-create">
<% if wz and lz then %>
<br />
<table class="cbi-section-table" style="width:700px; margin-left:5px">
<tr class="cbi-section-table-titles">
<th class="cbi-section-table-cell left" colspan="6"><%:New source NAT%>:</th>
</tr>
<tr class="cbi-section-table-descr">
<th class="cbi-section-table-cell"><%:Name%></th>
<th class="cbi-section-table-cell"><%:Source zone%></th>
<th class="cbi-section-table-cell"><%:Destination zone%></th>
<th class="cbi-section-table-cell"><%:To source IP%></th>
<th class="cbi-section-table-cell"><%:To source port%></th>
<th class="cbi-section-table-cell"></th>
</tr>
<tr class="cbi-section-table-row">
<td class="cbi-section-table-cell">
<input type="text" class="cbi-input-text" id="_newsnat.name" name="_newsnat.name" placeholder="<%:New SNAT rule%>" />
</td>
<td class="cbi-section-table-cell" style="width:110px">
<select class="cbi-input-text" id="_newsnat.src" name="_newsnat.src">
<% local k, v; for k, v in ipairs(fw:get_zones()) do -%>
<option<%=ifattr(v:name() == "lan", "selected", "selected")%> value="<%=v:name()%>"><%=v:name()%></option>
<%- end %>
</select>
</td>
<td class="cbi-section-table-cell" style="width:110px">
<select class="cbi-input-text" id="_newsnat.dest" name="_newsnat.dest">
<% local k, v; for k, v in ipairs(fw:get_zones()) do -%>
<option<%=ifattr(v:name() == "wan", "selected", "selected")%> value="<%=v:name()%>"><%=v:name()%></option>
<%- end %>
</select>
</td>
<td class="cbi-section-table-cell" style="width:110px">
<input type="text" class="cbi-input-text" id="_newsnat.dip" name="_newsnat.dip" />
</td>
<td class="cbi-section-table-cell" style="width:110px">
<input type="text" class="cbi-input-text" id="_newsnat.dport" name="_newsnat.dport" placeholder="<%:Do not rewrite%>" />
</td>
<td class="cbi-section-table-cell">
<input type="submit" class="cbi-button cbi-button-link" name="_newsnat.submit" value="<%:Add and edit...%>" />
</td>
</tr>
</table>
<script type="text/javascript">//<![CDATA[
cbi_validate_field('_newsnat.dport', true, 'portrange');
cbi_combobox_init('_newsnat.dip', {
<% local c, k, v = 0; for k, v in ipairs(nw:get_interfaces()) do -%>
<%- local a; for k, a in ipairs(v:ipaddrs()) do c = c + 1 -%>
<% if c > 1 then %>,<% end %>'<%=a:host():string()%>': '<%=a:host():string()%> (<%=v:shortname()%>)',
<%- end %>
<%- end %> }, '<%: -- Please choose -- %>', '<%: -- custom -- %>');
//]]></script>
<% else %>
<input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" value="<%:Add%>" />
<% end %>
</div>