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