c1a8a45c79649399d1fbaebc48b5e075ae88050d
[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(n)
41         if 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(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()
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(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(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 del_network(net)
127         local z
128         if net then
129                 for _, z in ipairs(get_zones()) do
130                         z:del_network(net)
131                 end
132         end
133 end
134
135
136 defaults = ub:usection("defaults")
137 defaults:property_bool("syn_flood")
138 defaults:property_bool("drop_invalid")
139 defaults:property("input")
140 defaults:property("forward")
141 defaults:property("output")
142
143
144 zone = ub:section("zone")
145 zone:property_bool("masq")
146 zone:property("name")
147 zone:property("network")
148 zone:property("input")
149 zone:property("forward")
150 zone:property("output")
151
152 function zone.add_network(self, net)
153         if ub.uci:get("network", net) == "interface" then
154                 local networks = ub:list(self:network() or self:name(), net)
155                 if #networks > 0 then
156                         self:network(table.concat(networks, " "))
157                 else
158                         self:network(" ")
159                 end
160         end
161 end
162
163 function zone.del_network(self, net)
164         local networks = ub:list(self:network() or self:name(), nil, net)
165         if #networks > 0 then
166                 self:network(table.concat(networks, " "))
167         else
168                 self:network(" ")
169         end
170 end
171
172 function zone.get_networks(self)
173         return ub:list(self:network() or self:name())
174 end
175
176 function zone.get_forwardings_by(self, what)
177         local name = self:name()
178         local forwards = { }
179         ub.uci:foreach("firewall", "forwarding",
180                 function(s)
181                         if s.src and s.dest and s[what] == name then
182                                 forwards[#forwards+1] = forwarding(s['.name'])
183                         end
184                 end)
185         return forwards
186 end
187
188 function zone.add_forwarding_to(self, dest, with_mtu_fix)
189         local exist, forward
190         for _, forward in ipairs(self:get_forwardings_by('src')) do
191                 if forward:dest() == dest then
192                         exist = true
193                         break
194                 end
195         end
196         if not exist and dest ~= self:name() then
197                 local s = ub.uci:section("firewall", "forwarding", nil, {
198                         src     = self:name(),
199                         dest    = dest,
200                         mtu_fix = with_mtu_fix and true or false
201                 })
202                 return s and forwarding(s)
203         end
204 end
205
206 function zone.add_forwarding_from(self, src, with_mtu_fix)
207         local exist, forward
208         for _, forward in ipairs(self:get_forwardings_by('dest')) do
209                 if forward:src() == src then
210                         exist = true
211                         break
212                 end
213         end
214         if not exist and src ~= self:name() then
215                 local s = ub.uci:section("firewall", "forwarding", nil, {
216                         src     = src,
217                         dest    = self:name(),
218                         mtu_fix = with_mtu_fix and true or false
219                 })
220                 return s and forwarding(s)
221         end
222 end
223
224 function zone.add_redirect(self, options)
225         options = options or { }
226         options.src = self:name()
227         local s = ub.uci:section("firewall", "redirect", nil, options)
228         return s and redirect(s)
229 end
230
231 function zone.add_rule(self, options)
232         options = options or { }
233         options.src = self:name()
234         local s = ub.uci:section("firewall", "rule", nil, options)
235         return s and rule(s)
236 end
237
238 function zone.get_color(self)
239         if self and self:name() == "lan" then
240                 return "#90f090"
241         elseif self and self:name() == "wan" then
242                 return "#f09090"
243         elseif self then
244                 math.randomseed(lmo.hash(self:name()))
245
246                 local r   = math.random(128)
247                 local g   = math.random(128)
248                 local min = 0
249                 local max = 128
250
251                 if ( r + g ) < 128 then
252                         min = 128 - r - g
253                 else
254                         max = 255 - r - g
255                 end
256
257                 local b = min + math.floor( math.random() * ( max - min ) )
258
259                 return "#%02x%02x%02x" % { 0xFF - r, 0xFF - g, 0xFF - b }
260         else
261                 return "#eeeeee"
262         end
263 end
264
265
266 forwarding = ub:section("forwarding")
267 forwarding:property_bool("mtu_fix")
268 forwarding:property("src")
269 forwarding:property("dest")
270
271 function forwarding.src_zone(self)
272         return zone(self:src())
273 end
274
275 function forwarding.dest_zone(self)
276         return zone(self:dest())
277 end
278
279
280 rule = ub:section("rule")
281 rule:property("src")
282 rule:property("src_ip")
283 rule:property("src_mac")
284 rule:property("src_port")
285 rule:property("dest")
286 rule:property("dest_ip")
287 rule:property("dest_port")
288 rule:property("proto")
289 rule:property("target")
290
291 function rule.src_zone(self)
292         return zone(self:src())
293 end
294
295
296 redirect = ub:section("redirect")
297 redirect:property("src")
298 redirect:property("src_ip")
299 redirect:property("src_mac")
300 redirect:property("src_port")
301 redirect:property("src_dport")
302 redirect:property("dest_ip")
303 redirect:property("dest_port")
304 redirect:property("proto")
305
306 function redirect.src_zone(self)
307         return zone(self:src())
308 end
309