luci-app-firewall: recognize egress rules in rule overview
[project/luci.git] / applications / luci-app-firewall / luasrc / tools / firewall.lua
1 -- Copyright 2011-2012 Jo-Philipp Wich <jow@openwrt.org>
2 -- Licensed to the public under the Apache License 2.0.
3
4 module("luci.tools.firewall", package.seeall)
5
6 local ut = require "luci.util"
7 local ip = require "luci.ip"
8 local nx = require "nixio"
9
10 local translate, translatef = luci.i18n.translate, luci.i18n.translatef
11
12 local function _(...)
13 return tostring(translate(...))
14 end
15
16 function fmt_neg(x)
17 if type(x) == "string" then
18 local v, neg = x:gsub("^ *! *", "")
19 if neg > 0 then
20 return v, "%s " % _("not")
21 else
22 return x, ""
23 end
24 end
25 return x, ""
26 end
27
28 function fmt_mac(x)
29 if x and #x > 0 then
30 local m, n
31 local l = { _("MAC"), " " }
32 for m in ut.imatch(x) do
33 m, n = fmt_neg(m)
34 l[#l+1] = "<var>%s%s</var>" %{ n, m }
35 l[#l+1] = ", "
36 end
37 if #l > 1 then
38 l[#l] = nil
39 if #l > 3 then
40 l[1] = _("MACs")
41 end
42 return table.concat(l, "")
43 end
44 end
45 end
46
47 function fmt_port(x, d)
48 if x and #x > 0 then
49 local p, n
50 local l = { _("port"), " " }
51 for p in ut.imatch(x) do
52 p, n = fmt_neg(p)
53 local a, b = p:match("(%d+)%D+(%d+)")
54 if a and b then
55 l[1] = _("ports")
56 l[#l+1] = "<var>%s%d-%d</var>" %{ n, a, b }
57 else
58 l[#l+1] = "<var>%s%d</var>" %{ n, p }
59 end
60 l[#l+1] = ", "
61 end
62 if #l > 1 then
63 l[#l] = nil
64 if #l > 3 then
65 l[1] = _("ports")
66 end
67 return table.concat(l, "")
68 end
69 end
70 return d and "<var>%s</var>" % d
71 end
72
73 function fmt_ip(x, d)
74 if x and #x > 0 then
75 local l = { _("IP"), " " }
76 local v, a, n
77 for v in ut.imatch(x) do
78 v, n = fmt_neg(v)
79 a, m = v:match("(%S+)/(%d+%.%S+)")
80 a = a or v
81 a = a:match(":") and ip.IPv6(a, m) or ip.IPv4(a, m)
82 if a and (a:is6() and a:prefix() < 128 or a:prefix() < 32) then
83 l[1] = _("IP range")
84 l[#l+1] = "<var title='%s - %s'>%s%s</var>" %{
85 a:minhost():string(),
86 a:maxhost():string(),
87 n, a:string()
88 }
89 else
90 l[#l+1] = "<var>%s%s</var>" %{
91 n,
92 a and a:string() or v
93 }
94 end
95 l[#l+1] = ", "
96 end
97 if #l > 1 then
98 l[#l] = nil
99 if #l > 3 then
100 l[1] = _("IPs")
101 end
102 return table.concat(l, "")
103 end
104 end
105 return d and "<var>%s</var>" % d
106 end
107
108 function fmt_zone(x, d)
109 if x == "*" then
110 return "<var>%s</var>" % _("any zone")
111 elseif x and #x > 0 then
112 return "<var>%s</var>" % x
113 elseif d then
114 return "<var>%s</var>" % d
115 end
116 end
117
118 function fmt_icmp_type(x)
119 if x and #x > 0 then
120 local t, v, n
121 local l = { _("type"), " " }
122 for v in ut.imatch(x) do
123 v, n = fmt_neg(v)
124 l[#l+1] = "<var>%s%s</var>" %{ n, v }
125 l[#l+1] = ", "
126 end
127 if #l > 1 then
128 l[#l] = nil
129 if #l > 3 then
130 l[1] = _("types")
131 end
132 return table.concat(l, "")
133 end
134 end
135 end
136
137 function fmt_proto(x, icmp_types)
138 if x and #x > 0 then
139 local v, n
140 local l = { }
141 local t = fmt_icmp_type(icmp_types)
142 for v in ut.imatch(x) do
143 v, n = fmt_neg(v)
144 if v == "tcpudp" then
145 l[#l+1] = "TCP"
146 l[#l+1] = ", "
147 l[#l+1] = "UDP"
148 l[#l+1] = ", "
149 elseif v ~= "all" then
150 local p = nx.getproto(v)
151 if p then
152 -- ICMP
153 if (p.proto == 1 or p.proto == 58) and t then
154 l[#l+1] = translatef(
155 "%s%s with %s",
156 n, p.aliases[1] or p.name, t
157 )
158 else
159 l[#l+1] = "%s%s" %{
160 n,
161 p.aliases[1] or p.name
162 }
163 end
164 l[#l+1] = ", "
165 end
166 end
167 end
168 if #l > 0 then
169 l[#l] = nil
170 return table.concat(l, "")
171 end
172 end
173 end
174
175 function fmt_limit(limit, burst)
176 burst = tonumber(burst)
177 if limit and #limit > 0 then
178 local l, u = limit:match("(%d+)/(%w+)")
179 l = tonumber(l or limit)
180 u = u or "second"
181 if l then
182 if u:match("^s") then
183 u = _("second")
184 elseif u:match("^m") then
185 u = _("minute")
186 elseif u:match("^h") then
187 u = _("hour")
188 elseif u:match("^d") then
189 u = _("day")
190 end
191 if burst and burst > 0 then
192 return translatef("<var>%d</var> pkts. per <var>%s</var>, \
193 burst <var>%d</var> pkts.", l, u, burst)
194 else
195 return translatef("<var>%d</var> pkts. per <var>%s</var>", l, u)
196 end
197 end
198 end
199 end
200
201 function fmt_target(x, src, dest)
202 if not src or #src == 0 then
203 if x == "ACCEPT" then
204 return _("Accept output")
205 elseif x == "REJECT" then
206 return _("Refuse output")
207 elseif x == "NOTRACK" then
208 return _("Do not track output")
209 else --if x == "DROP" then
210 return _("Discard output")
211 end
212 elseif dest and #dest > 0 then
213 if x == "ACCEPT" then
214 return _("Accept forward")
215 elseif x == "REJECT" then
216 return _("Refuse forward")
217 elseif x == "NOTRACK" then
218 return _("Do not track forward")
219 else --if x == "DROP" then
220 return _("Discard forward")
221 end
222 else
223 if x == "ACCEPT" then
224 return _("Accept input")
225 elseif x == "REJECT" then
226 return _("Refuse input")
227 elseif x == "NOTRACK" then
228 return _("Do not track input")
229 else --if x == "DROP" then
230 return _("Discard input")
231 end
232 end
233 end
234
235
236 function opt_enabled(s, t, ...)
237 if t == luci.cbi.Button then
238 local o = s:option(t, "__enabled")
239 function o.render(self, section)
240 if self.map:get(section, "enabled") ~= "0" then
241 self.title = _("Rule is enabled")
242 self.inputtitle = _("Disable")
243 self.inputstyle = "reset"
244 else
245 self.title = _("Rule is disabled")
246 self.inputtitle = _("Enable")
247 self.inputstyle = "apply"
248 end
249 t.render(self, section)
250 end
251 function o.write(self, section, value)
252 if self.map:get(section, "enabled") ~= "0" then
253 self.map:set(section, "enabled", "0")
254 else
255 self.map:del(section, "enabled")
256 end
257 end
258 return o
259 else
260 local o = s:option(t, "enabled", ...)
261 o.default = "1"
262 return o
263 end
264 end
265
266 function opt_name(s, t, ...)
267 local o = s:option(t, "name", ...)
268
269 function o.cfgvalue(self, section)
270 return self.map:get(section, "name") or
271 self.map:get(section, "_name") or "-"
272 end
273
274 function o.write(self, section, value)
275 if value ~= "-" then
276 self.map:set(section, "name", value)
277 self.map:del(section, "_name")
278 else
279 self:remove(section)
280 end
281 end
282
283 function o.remove(self, section)
284 self.map:del(section, "name")
285 self.map:del(section, "_name")
286 end
287
288 return o
289 end