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