libs/core: add luci.model.firewall, oop abstraction for uci firewall
[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