libs/core: use imatch() in firewall and network model
[project/luci.git] / libs / core / luasrc / model / firewall.lua
1 --[[
2 LuCI - Firewall model
3
4 Copyright 2009 Jo-Philipp Wich <xm@subsignal.org>
5
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17
18 ]]--
19
20 local type, pairs, ipairs, table, luci, math
21 = type, pairs, ipairs, table, luci, math
22
23 local lmo = require "lmo"
24 local utl = require "luci.util"
25 local uci = require "luci.model.uci"
26
27 module "luci.model.firewall"
28
29
30 local uci_r, uci_s
31
32 function _valid_id(x)
33 return (x and #x > 0 and x:match("^[a-zA-Z0-9_]+$"))
34 end
35
36 function _get(c, s, o)
37 return uci_r:get(c, s, o)
38 end
39
40 function _set(c, s, o, v)
41 if v ~= nil then
42 if type(v) == "boolean" then v = v and "1" or "0" end
43 return uci_r:set(c, s, o, v)
44 else
45 return uci_r:del(c, s, o, v)
46 end
47 end
48
49
50 function init(cursor)
51 uci_r = cursor or uci_r or uci.cursor()
52 uci_s = uci_r:substate()
53
54 return _M
55 end
56
57 function save(self, ...)
58 uci_r:save(...)
59 uci_r:load(...)
60 end
61
62 function commit(self, ...)
63 uci_r:commit(...)
64 uci_r:load(...)
65 end
66
67 function add_zone(self, n)
68 if _valid_id(n) and not self:get_zone(n) then
69 local z = uci_r:section("firewall", "zone", nil, {
70 name = n,
71 network = " ",
72 input = defaults:input() or "DROP",
73 forward = defaults:forward() or "DROP",
74 output = defaults:output() or "DROP"
75 })
76
77 return z and zone(z)
78 end
79 end
80
81 function get_zone(self, n)
82 if uci_r:get("firewall", n) == "zone" then
83 return zone(n)
84 else
85 local z
86 uci_r:foreach("firewall", "zone",
87 function(s)
88 if n and s.name == n then
89 z = s['.name']
90 return false
91 end
92 end)
93 return z and zone(z)
94 end
95 end
96
97 function get_zones(self)
98 local zones = { }
99 local znl = { }
100
101 uci_r:foreach("firewall", "zone",
102 function(s)
103 if s.name then
104 znl[s.name] = zone(s['.name'])
105 end
106 end)
107
108 local z
109 for z in utl.kspairs(znl) do
110 zones[#zones+1] = znl[z]
111 end
112
113 return zones
114 end
115
116 function get_zone_by_network(self, net)
117 local z
118
119 uci_r:foreach("firewall", "zone",
120 function(s)
121 if s.name and net then
122 local n
123 for n in utl.imatch(s.network or s.name) do
124 if n == net then
125 z = s['.name']
126 return false
127 end
128 end
129 end
130 end)
131
132 return z and zone(z)
133 end
134
135 function del_zone(self, n)
136 local r = false
137
138 if uci_r:get("firewall", n) == "zone" then
139 local z = uci_r:get("firewall", n, "name")
140 r = uci_r:delete("firwall", n)
141 n = z
142 else
143 uci_r:foreach("firewall", "zone",
144 function(s)
145 if n and s.name == n then
146 r = uci_r:delete("firewall", s['.name'])
147 return false
148 end
149 end)
150 end
151
152 if r then
153 uci_r:foreach("firewall", "rule",
154 function(s)
155 if s.src == n or s.dest == n then
156 uci_r:delete("firewall", s['.name'])
157 end
158 end)
159
160 uci_r:foreach("firewall", "redirect",
161 function(s)
162 if s.src == n then
163 uci_r:delete("firewall", s['.name'])
164 end
165 end)
166
167 uci_r:foreach("firewall", "forwarding",
168 function(s)
169 if s.src == n then
170 uci_r:delete("firewall", s['.name'])
171 end
172 end)
173 end
174
175 return r
176 end
177
178 function rename_zone(self, old, new)
179 local r = false
180
181 if _valid_id(new) and not self:get_zone(new) then
182 uci_r:foreach("firewall", "zone",
183 function(s)
184 if n and s.name == old then
185 uci_r:set("firewall", s['.name'], "name", new)
186 r = true
187 return false
188 end
189 end)
190
191 if r then
192 uci_r:foreach("firewall", "rule",
193 function(s)
194 if s.src == old then
195 uci_r:set("firewall", s['.name'], "src", new)
196 end
197 if s.dest == old then
198 uci_r:set("firewall", s['.name'], "dest", new)
199 end
200 end)
201
202 uci_r:foreach("firewall", "redirect",
203 function(s)
204 if s.src == old then
205 uci_r:set("firewall", s['.name'], "src", new)
206 end
207 if s.dest == old then
208 uci_r:set("firewall", s['.name'], "dest", new)
209 end
210 end)
211
212 ub.uci:foreach("firewall", "forwarding",
213 function(s)
214 if s.src == old then
215 ub.uci:set("firewall", s['.name'], "src", new)
216 end
217 if s.dest == old then
218 uci_r:set("firewall", s['.name'], "dest", new)
219 end
220 end)
221 end
222 end
223
224 return r
225 end
226
227 function del_network(self, net)
228 local z
229 if net then
230 for _, z in ipairs(self:get_zones()) do
231 z:del_network(net)
232 end
233 end
234 end
235
236
237 defaults = utl.class()
238 function defaults.__init__(self)
239 uci_r:foreach("firewall", "defaults",
240 function(s)
241 self.sid = s['.name']
242 return false
243 end)
244
245 self.sid = self.sid or uci_r:section("firewall", "defaults", nil, { })
246 end
247
248 function defaults.get(self, opt)
249 return _get("firewall", self.sid, opt)
250 end
251
252 function defaults.set(self, opt, val)
253 return _set("firewall", self.sid, opt, val)
254 end
255
256 function defaults.syn_flood(self)
257 return (self:get("syn_flood") == "1")
258 end
259
260 function defaults.drop_invalid(self)
261 return (self:get("drop_invalid") == "1")
262 end
263
264 function defaults.input(self)
265 return self:get("input") or "DROP"
266 end
267
268 function defaults.forward(self)
269 return self:get("forward") or "DROP"
270 end
271
272 function defaults.output(self)
273 return self:get("output") or "DROP"
274 end
275
276
277 zone = utl.class()
278 function zone.__init__(self, z)
279 if uci_r:get("firewall", z) == "zone" then
280 self.sid = z
281 self.data = uci_r:get_all("firewall", z)
282 else
283 uci_r:foreach("firewall", "zone",
284 function(s)
285 if s.name == z then
286 self.sid = s['.name']
287 self.data = s
288 return false
289 end
290 end)
291 end
292 end
293
294 function zone.get(self, opt)
295 return _get("firewall", self.sid, opt)
296 end
297
298 function zone.set(self, opt, val)
299 return _set("firewall", self.sid, opt, val)
300 end
301
302 function zone.masq(self)
303 return (self:get("masq") == "1")
304 end
305
306 function zone.name(self)
307 return self:get("name")
308 end
309
310 function zone.network(self)
311 return self:get("network")
312 end
313
314 function zone.input(self)
315 return self:get("input") or "DROP"
316 end
317
318 function zone.forward(self)
319 return self:get("forward") or "DROP"
320 end
321
322 function zone.output(self)
323 return self:get("output") or "DROP"
324 end
325
326 function zone.add_network(self, net)
327 if uci_r:get("network", net) == "interface" then
328 local nets = { }
329
330 local n
331 for n in utl.imatch(self:get("network") or self:get("name")) do
332 if n ~= net then
333 nets[#nets+1] = n
334 end
335 end
336
337 nets[#nets+1] = net
338
339 if #nets > 0 then
340 self:set("network", table.concat(nets, " "))
341 else
342 self:set("network", " ")
343 end
344 end
345 end
346
347 function zone.del_network(self, net)
348 local nets = { }
349
350 local n
351 for n in utl.imatch(self:get("network") or self:get("name")) do
352 if n ~= net then
353 nets[#nets+1] = n
354 end
355 end
356
357 if #nets > 0 then
358 self:set("network", table.concat(nets, " "))
359 else
360 self:set("network", " ")
361 end
362 end
363
364 function zone.get_networks(self)
365 local nets = { }
366
367 local n
368 for n in utl.imatch(self:get("network") or self:get("name")) do
369 nets[#nets+1] = n
370 end
371
372 return nets
373 end
374
375 function zone.get_forwardings_by(self, what)
376 local name = self:name()
377 local forwards = { }
378
379 uci_r:foreach("firewall", "forwarding",
380 function(s)
381 if s.src and s.dest and s[what] == name then
382 forwards[#forwards+1] = forwarding(s['.name'])
383 end
384 end)
385
386 return forwards
387 end
388
389 function zone.add_forwarding_to(self, dest)
390 local exist, forward
391
392 for _, forward in ipairs(self:get_forwardings_by('src')) do
393 if forward:dest() == dest then
394 exist = true
395 break
396 end
397 end
398
399 if not exist and dest ~= self:name() then
400 local s = uci_r:section("firewall", "forwarding", nil, {
401 src = self:name(),
402 dest = dest
403 })
404
405 return s and forwarding(s)
406 end
407 end
408
409 function zone.add_forwarding_from(self, src)
410 local exist, forward
411
412 for _, forward in ipairs(self:get_forwardings_by('dest')) do
413 if forward:src() == src then
414 exist = true
415 break
416 end
417 end
418
419 if not exist and src ~= self:name() then
420 local s = uci_r:section("firewall", "forwarding", nil, {
421 src = src,
422 dest = self:name()
423 })
424
425 return s and forwarding(s)
426 end
427 end
428
429 function zone.del_forwardings_by(self, what)
430 local name = self:name()
431
432 uci_r:foreach("firewall", "forwarding",
433 function(s)
434 if s.src and s.dest and s[what] == name then
435 uci_r:delete("firewall", s['.name'])
436 end
437 end)
438 end
439
440 function zone.add_redirect(self, options)
441 options = options or { }
442 options.src = self:name()
443
444 local s = uci_r:section("firewall", "redirect", nil, options)
445 return s and redirect(s)
446 end
447
448 function zone.add_rule(self, options)
449 options = options or { }
450 options.src = self:name()
451
452 local s = uci_r:section("firewall", "rule", nil, options)
453 return s and rule(s)
454 end
455
456 function zone.get_color(self)
457 if self and self:name() == "lan" then
458 return "#90f090"
459 elseif self and self:name() == "wan" then
460 return "#f09090"
461 elseif self then
462 math.randomseed(lmo.hash(self:name()))
463
464 local r = math.random(128)
465 local g = math.random(128)
466 local min = 0
467 local max = 128
468
469 if ( r + g ) < 128 then
470 min = 128 - r - g
471 else
472 max = 255 - r - g
473 end
474
475 local b = min + math.floor( math.random() * ( max - min ) )
476
477 return "#%02x%02x%02x" % { 0xFF - r, 0xFF - g, 0xFF - b }
478 else
479 return "#eeeeee"
480 end
481 end
482
483
484 forwarding = utl.class()
485 function forwarding.__init__(self, f)
486 self.sid = f
487 end
488
489 function forwarding.src(self)
490 return uci_r:get("firewall", self.sid, "src")
491 end
492
493 function forwarding.dest(self)
494 return uci_r:get("firewall", self.sid, "dest")
495 end
496
497 function forwarding.src_zone(self)
498 return zone(self:src())
499 end
500
501 function forwarding.dest_zone(self)
502 return zone(self:dest())
503 end
504
505
506 rule = utl.class()
507 function rule.__init__(self, f)
508 self.sid = f
509 end
510
511 function rule.get(self, opt)
512 return _get("firewall", self.sid, opt)
513 end
514
515 function rule.set(self, opt, val)
516 return _set("firewall", self.sid, opt, val)
517 end
518
519 function rule.src(self)
520 return uci_r:get("firewall", self.sid, "src")
521 end
522
523 function rule.dest(self)
524 return uci_r:get("firewall", self.sid, "dest")
525 end
526
527 function rule.src_zone(self)
528 return zone(self:src())
529 end
530
531 function rule.dest_zone(self)
532 return zone(self:dest())
533 end
534
535
536 redirect = utl.class()
537 function redirect.__init__(self, f)
538 self.sid = f
539 end
540
541 function redirect.get(self, opt)
542 return _get("firewall", self.sid, opt)
543 end
544
545 function redirect.set(self, opt, val)
546 return _set("firewall", self.sid, opt, val)
547 end
548
549 function redirect.src(self)
550 return uci_r:get("firewall", self.sid, "src")
551 end
552
553 function redirect.dest(self)
554 return uci_r:get("firewall", self.sid, "dest")
555 end
556
557 function redirect.src_zone(self)
558 return zone(self:src())
559 end
560
561 function redirect.dest_zone(self)
562 return zone(self:dest())
563 end