libs/core: luci.model.firewall: make top level functions instance methods to keep...
[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 uct = require "luci.model.uci.bind"
26
27 module "luci.model.firewall"
28
29
30 local ub = uct.bind("firewall")
31
32 function init(cursor)
33         if cursor then
34                 cursor:unload("firewall")
35                 cursor:load("firewall")
36                 ub:init(cursor)
37         end
38 end
39
40 function add_zone(self, n)
41         if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not self:get_zone(n) then
42                 local z = ub.uci:section("firewall", "zone", nil, {
43                         name    = n,
44                         network = " ",
45                         input   = defaults:input()   or "DROP",
46                         forward = defaults:forward() or "DROP",
47                         output  = defaults:output()  or "DROP"
48                 })
49
50                 return z and zone(z)
51         end
52 end
53
54 function get_zone(self, n)
55         local z
56         ub.uci:foreach("firewall", "zone",
57                 function(s)
58                         if n and s.name == n then
59                                 z = s['.name']
60                                 return false
61                         end
62                 end)
63         return z and zone(z)
64 end
65
66 function get_zones(self)
67         local zones = { }
68         ub.uci:foreach("firewall", "zone",
69                 function(s)
70                         if s.name then
71                                 zones[#zones+1] = zone(s['.name'])
72                         end
73                 end)
74         return zones
75 end
76
77 function get_zones_by_network(self, net)
78         local zones = { }
79         ub.uci:foreach("firewall", "zone",
80                 function(s)
81                         if s.name then
82                                 local n
83                                 for _, n in ipairs(ub:list(s.network or s.name)) do
84                                         if n == net then
85                                                 zones[#zones+1] = zone(s['.name'])
86                                                 return true
87                                         end
88                                 end
89                         end
90                 end)
91         return zones
92 end
93
94 function del_zone(self, n)
95         local r = false
96         ub.uci:foreach("firewall", "zone",
97                 function(s)
98                         if n and s.name == n then
99                                 r = ub.uci:delete("firewall", s['.name'])
100                                 return false
101                         end
102                 end)
103         if r then
104                 ub.uci:foreach("firewall", "rule",
105                         function(s)
106                                 if s.src == n or s.dest == n then
107                                         ub.uci:delete("firewall", s['.name'])
108                                 end
109                         end)
110                 ub.uci:foreach("firewall", "redirect",
111                         function(s)
112                                 if s.src == n then
113                                         ub.uci:delete("firewall", s['.name'])
114                                 end
115                         end)
116                 ub.uci:foreach("firewall", "forwarding",
117                         function(s)
118                                 if s.src == n then
119                                         ub.uci:delete("firewall", s['.name'])
120                                 end
121                         end)
122         end
123         return r
124 end
125
126 function rename_zone(self, old, new)
127         local r = false
128         if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_zone(new) then
129                 ub.uci:foreach("firewall", "zone",
130                         function(s)
131                                 if n and s.name == old then
132                                         ub.uci:set("firewall", s['.name'], "name", new)
133                                         r = true
134                                         return false
135                                 end
136                         end)
137                 if r then
138                         ub.uci:foreach("firewall", "rule",
139                                 function(s)
140                                         if s.src == old then
141                                                 ub.uci:set("firewall", s['.name'], "src", new)
142                                         elseif s.dest == old then
143                                                 ub.uci:set("firewall", s['.name'], "dest", new)
144                                         end
145                                 end)
146                         ub.uci:foreach("firewall", "redirect",
147                                 function(s)
148                                         if s.src == old then
149                                                 ub.uci:set("firewall", s['.name'], "src", new)
150                                         end
151                                 end)
152                         ub.uci:foreach("firewall", "forwarding",
153                                 function(s)
154                                         if s.src == old then
155                                                 ub.uci:set("firewall", s['.name'], "src", new)
156                                         end
157                                 end)
158                 end
159         end
160         return r
161 end
162
163 function del_network(self, net)
164         local z
165         if net then
166                 for _, z in ipairs(self:get_zones()) do
167                         z:del_network(net)
168                 end
169         end
170 end
171
172
173 defaults = ub:usection("defaults")
174 defaults:property_bool("syn_flood")
175 defaults:property_bool("drop_invalid")
176 defaults:property("input")
177 defaults:property("forward")
178 defaults:property("output")
179
180
181 zone = ub:section("zone")
182 zone:property_bool("masq")
183 zone:property("name")
184 zone:property("network")
185 zone:property("input")
186 zone:property("forward")
187 zone:property("output")
188
189 function zone.add_network(self, net)
190         if ub.uci:get("network", net) == "interface" then
191                 local networks = ub:list(self:network() or self:name(), net)
192                 if #networks > 0 then
193                         self:network(table.concat(networks, " "))
194                 else
195                         self:network(" ")
196                 end
197         end
198 end
199
200 function zone.del_network(self, net)
201         local networks = ub:list(self:network() or self:name(), nil, net)
202         if #networks > 0 then
203                 self:network(table.concat(networks, " "))
204         else
205                 self:network(" ")
206         end
207 end
208
209 function zone.get_networks(self)
210         return ub:list(self:network() or self:name())
211 end
212
213 function zone.get_forwardings_by(self, what)
214         local name = self:name()
215         local forwards = { }
216         ub.uci:foreach("firewall", "forwarding",
217                 function(s)
218                         if s.src and s.dest and s[what] == name then
219                                 forwards[#forwards+1] = forwarding(s['.name'])
220                         end
221                 end)
222         return forwards
223 end
224
225 function zone.add_forwarding_to(self, dest, with_mtu_fix)
226         local exist, forward
227         for _, forward in ipairs(self:get_forwardings_by('src')) do
228                 if forward:dest() == dest then
229                         exist = true
230                         break
231                 end
232         end
233         if not exist and dest ~= self:name() then
234                 local s = ub.uci:section("firewall", "forwarding", nil, {
235                         src     = self:name(),
236                         dest    = dest,
237                         mtu_fix = with_mtu_fix and true or false
238                 })
239                 return s and forwarding(s)
240         end
241 end
242
243 function zone.add_forwarding_from(self, src, with_mtu_fix)
244         local exist, forward
245         for _, forward in ipairs(self:get_forwardings_by('dest')) do
246                 if forward:src() == src then
247                         exist = true
248                         break
249                 end
250         end
251         if not exist and src ~= self:name() then
252                 local s = ub.uci:section("firewall", "forwarding", nil, {
253                         src     = src,
254                         dest    = self:name(),
255                         mtu_fix = with_mtu_fix and true or false
256                 })
257                 return s and forwarding(s)
258         end
259 end
260
261 function zone.add_redirect(self, options)
262         options = options or { }
263         options.src = self:name()
264         local s = ub.uci:section("firewall", "redirect", nil, options)
265         return s and redirect(s)
266 end
267
268 function zone.add_rule(self, options)
269         options = options or { }
270         options.src = self:name()
271         local s = ub.uci:section("firewall", "rule", nil, options)
272         return s and rule(s)
273 end
274
275 function zone.get_color(self)
276         if self and self:name() == "lan" then
277                 return "#90f090"
278         elseif self and self:name() == "wan" then
279                 return "#f09090"
280         elseif self then
281                 math.randomseed(lmo.hash(self:name()))
282
283                 local r   = math.random(128)
284                 local g   = math.random(128)
285                 local min = 0
286                 local max = 128
287
288                 if ( r + g ) < 128 then
289                         min = 128 - r - g
290                 else
291                         max = 255 - r - g
292                 end
293
294                 local b = min + math.floor( math.random() * ( max - min ) )
295
296                 return "#%02x%02x%02x" % { 0xFF - r, 0xFF - g, 0xFF - b }
297         else
298                 return "#eeeeee"
299         end
300 end
301
302
303 forwarding = ub:section("forwarding")
304 forwarding:property_bool("mtu_fix")
305 forwarding:property("src")
306 forwarding:property("dest")
307
308 function forwarding.src_zone(self)
309         return zone(self:src())
310 end
311
312 function forwarding.dest_zone(self)
313         return zone(self:dest())
314 end
315
316
317 rule = ub:section("rule")
318 rule:property("src")
319 rule:property("src_ip")
320 rule:property("src_mac")
321 rule:property("src_port")
322 rule:property("dest")
323 rule:property("dest_ip")
324 rule:property("dest_port")
325 rule:property("proto")
326 rule:property("target")
327
328 function rule.src_zone(self)
329         return zone(self:src())
330 end
331
332
333 redirect = ub:section("redirect")
334 redirect:property("src")
335 redirect:property("src_ip")
336 redirect:property("src_mac")
337 redirect:property("src_port")
338 redirect:property("src_dport")
339 redirect:property("dest_ip")
340 redirect:property("dest_port")
341 redirect:property("proto")
342
343 function redirect.src_zone(self)
344         return zone(self:src())
345 end
346