713fc57048a373bc699977613dd54f6e82995107
[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