libs/core: fixes for luci.model.firewall, luci.model.network and luci.model.network...
[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_zone_by_network(self, net)
78         local z
79         ub.uci:foreach("firewall", "zone",
80                 function(s)
81                         if s.name and net then
82                                 local n
83                                 for _, n in ipairs(ub:list(s.network or s.name)) do
84                                         if n == net then
85                                                 z = s['.name']
86                                                 return false
87                                         end
88                                 end
89                         end
90                 end)
91         return z and zone(z)
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 "1" or "0"
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 "1" or "0"
256                 })
257                 return s and forwarding(s)
258         end
259 end
260
261 function zone.del_forwardings_by(self, what)
262         local name = self:name()
263         ub.uci:foreach("firewall", "forwarding",
264                 function(s)
265                         if s.src and s.dest and s[what] == name then
266                                 ub.uci:delete("firewall", s['.name'])
267                         end
268                 end)
269 end
270
271 function zone.add_redirect(self, options)
272         options = options or { }
273         options.src = self:name()
274         local s = ub.uci:section("firewall", "redirect", nil, options)
275         return s and redirect(s)
276 end
277
278 function zone.add_rule(self, options)
279         options = options or { }
280         options.src = self:name()
281         local s = ub.uci:section("firewall", "rule", nil, options)
282         return s and rule(s)
283 end
284
285 function zone.get_color(self)
286         if self and self:name() == "lan" then
287                 return "#90f090"
288         elseif self and self:name() == "wan" then
289                 return "#f09090"
290         elseif self then
291                 math.randomseed(lmo.hash(self:name()))
292
293                 local r   = math.random(128)
294                 local g   = math.random(128)
295                 local min = 0
296                 local max = 128
297
298                 if ( r + g ) < 128 then
299                         min = 128 - r - g
300                 else
301                         max = 255 - r - g
302                 end
303
304                 local b = min + math.floor( math.random() * ( max - min ) )
305
306                 return "#%02x%02x%02x" % { 0xFF - r, 0xFF - g, 0xFF - b }
307         else
308                 return "#eeeeee"
309         end
310 end
311
312
313 forwarding = ub:section("forwarding")
314 forwarding:property_bool("mtu_fix")
315 forwarding:property("src")
316 forwarding:property("dest")
317
318 function forwarding.src_zone(self)
319         return zone(self:src())
320 end
321
322 function forwarding.dest_zone(self)
323         return zone(self:dest())
324 end
325
326
327 rule = ub:section("rule")
328 rule:property("src")
329 rule:property("src_ip")
330 rule:property("src_mac")
331 rule:property("src_port")
332 rule:property("dest")
333 rule:property("dest_ip")
334 rule:property("dest_port")
335 rule:property("proto")
336 rule:property("target")
337
338 function rule.src_zone(self)
339         return zone(self:src())
340 end
341
342
343 redirect = ub:section("redirect")
344 redirect:property("src")
345 redirect:property("src_ip")
346 redirect:property("src_mac")
347 redirect:property("src_port")
348 redirect:property("src_dport")
349 redirect:property("dest_ip")
350 redirect:property("dest_port")
351 redirect:property("proto")
352
353 function redirect.src_zone(self)
354         return zone(self:src())
355 end
356