luci-app-siitwizard: explicitely require bit library
[project/luci.git] / applications / luci-app-siitwizard / luasrc / model / cbi / siitwizard.lua
1 --[[
2 LuCI - Lua Configuration Interface
3
4 Copyright 2008 Steven Barth <steven@midlink.org>
5 Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14
15 ]]--
16
17 local uci = require "luci.model.uci".cursor()
18 local bit = require "nixio".bit
19
20 -------------------- Init --------------------
21
22 --
23 -- Find link-local address
24 --
25 LL_PREFIX = luci.ip.IPv6("fe80::/64")
26 function find_ll()
27         for _, r in ipairs(luci.sys.net.routes6()) do
28                 if LL_PREFIX:contains(r.dest) and r.dest:higher(LL_PREFIX) then
29                         return r.dest:sub(LL_PREFIX)
30                 end
31         end
32         return luci.ip.IPv6("::")
33 end
34
35 --
36 -- Determine defaults
37 --
38 local ula_prefix  = uci:get("siit", "ipv6", "ula_prefix")  or "fd00::"
39 local ula_global  = uci:get("siit", "ipv6", "ula_global")  or "00ca:ffee:babe::"                -- = Freifunk
40 local ula_subnet  = uci:get("siit", "ipv6", "ula_subnet")  or "0000:0000:0000:4223::"   -- = Berlin
41 local siit_prefix = uci:get("siit", "ipv6", "siit_prefix") or "::ffff:0000:0000"
42 local ipv4_pool   = uci:get("siit", "ipv4", "pool")        or "172.16.0.0/12"
43 local ipv4_netsz  = uci:get("siit", "ipv4", "netsize")     or "24"
44
45 --
46 -- Find IPv4 allocation pool
47 --
48 local gv4_net = luci.ip.IPv4(ipv4_pool)
49
50 --
51 -- Generate ULA
52 --
53 local ula = luci.ip.IPv6("::/64")
54
55 for _, prefix in ipairs({ ula_prefix, ula_global, ula_subnet }) do
56         ula = ula:add(luci.ip.IPv6(prefix))
57 end
58
59 ula = ula:add(find_ll())
60
61
62 -------------------- View --------------------
63 f = SimpleForm("siitwizward", "SIIT-Wizzard",
64  "This wizzard helps to setup SIIT (IPv4-over-IPv6) translation according to RFC2765.")
65
66 f:field(DummyValue, "info_ula", "Mesh ULA address").value = ula:string()
67
68 f:field(DummyValue, "ipv4_pool", "IPv4 allocation pool").value =
69         "%s (%i hosts)" %{ gv4_net:string(), 2 ^ ( 32 - gv4_net:prefix() ) - 2 }
70
71 f:field(DummyValue, "ipv4_size", "IPv4 LAN network prefix").value =
72         "%i bit (%i hosts)" %{ ipv4_netsz, 2 ^ ( 32 - ipv4_netsz ) - 2 }
73
74 mode = f:field(ListValue, "mode", "Operation mode")
75 mode:value("client", "Client")
76 mode:value("gateway", "Gateway")
77
78 dev = f:field(ListValue, "device", "Wireless device")
79 uci:foreach("wireless", "wifi-device",
80         function(section)
81                 dev:value(section[".name"])
82         end)
83
84 lanip = f:field(Value, "ipaddr", "LAN IPv4 subnet")
85 function lanip.formvalue(self, section)
86         local val = self.map:formvalue(self:cbid(section))
87         local net = luci.ip.IPv4("%s/%i" %{ val, ipv4_netsz })
88
89         if net then
90                 if gv4_net:contains(net) then
91                         if not net:minhost():equal(net:host()) then
92                                 self.error = { [section] = true }
93                                 f.errmessage = "IPv4 address is not the first host of " ..
94                                         "subnet, expected " .. net:minhost():string()
95                         end
96                 else
97                         self.error = { [section] = true }
98                         f.errmessage = "IPv4 address is not within the allocation pool"
99                 end
100         else
101                 self.error = { [section] = true }
102                 f.errmessage = "Invalid IPv4 address given"
103         end
104
105         return val
106 end
107
108 dns = f:field(Value, "dns", "DNS server for LAN clients")
109 dns.value = "141.1.1.1"
110
111 -------------------- Control --------------------
112 function f.handle(self, state, data)
113         if state == FORM_VALID then
114                 luci.http.redirect(luci.dispatcher.build_url("admin", "uci", "changes"))
115                 return false
116         end
117         return true
118 end
119
120 function mode.write(self, section, value)
121
122         --
123         -- Find LAN IPv4 range
124         --
125         local lan_net = luci.ip.IPv4(
126                 ( lanip:formvalue(section) or "172.16.0.1" ) .. "/" .. ipv4_netsz
127         )
128
129         if not lan_net then return end
130
131         --
132         -- Find wifi interface, dns server and hostname
133         --
134         local device      = dev:formvalue(section)
135         local dns_server  = dns:formvalue(section) or "141.1.1.1"
136         local hostname    = "siit-" .. lan_net:host():string():gsub("%.","-")
137
138         --
139         -- Configure wifi device
140         --
141         local wifi_device  = dev:formvalue(section)
142         local wifi_essid   = uci:get("siit", "wifi", "essid")   or "6mesh.freifunk.net"
143         local wifi_bssid   = uci:get("siit", "wifi", "bssid")   or "02:ca:ff:ee:ba:be"
144         local wifi_channel = uci:get("siit", "wifi", "channel") or "1"
145
146         -- nuke old device definition
147         uci:delete_all("wireless", "wifi-iface",
148                 function(s) return s.device == wifi_device end )
149
150         uci:delete_all("network", "interface",
151                 function(s) return s['.name'] == wifi_device end )
152
153         -- create wifi device definition
154         uci:tset("wireless", wifi_device, {
155                 disabled  = 0,
156                 channel   = wifi_channel,
157 --              txantenna = 1,
158 --              rxantenna = 1,
159 --              diversity = 0
160         })
161
162         uci:section("wireless", "wifi-iface", nil, {
163                 encryption = "none",
164                 mode       = "adhoc",
165                 txpower    = 10,
166                 sw_merge   = 1,
167                 network    = wifi_device,
168                 device     = wifi_device,
169                 ssid       = wifi_essid,
170                 bssid      = wifi_bssid,
171         })
172
173         --
174         -- Gateway mode
175         --
176         --      * wan port is dhcp, lan port is 172.23.1.1/24
177         --      * siit0 gets a dummy address: 169.254.42.42
178         --      * wl0 gets an ipv6 address, in this case the fdca:ffee:babe::1:1/64
179         --      * we do a ::ffff:ffff:0/96 route into siit0, so everything from 6mesh goes into translation.
180         --      * an HNA6 of ::ffff:ffff:0:0/96 announces the mapped 0.0.0.0/0 ipv4 space.
181         --      * MTU on WAN, LAN down to 1400, ipv6 headers are slighly larger.
182
183         if value == "gateway" then
184
185                 -- wan mtu
186                 uci:set("network", "wan", "mtu", 1240)
187
188                 -- lan settings
189                 uci:tset("network", "lan", {
190                         mtu     = 1240,
191                         ipaddr  = lan_net:host():string(),
192                         netmask = lan_net:mask():string(),
193                         proto   = "static"
194                 })
195
196                 -- use full siit subnet
197                 siit_route = luci.ip.IPv6(siit_prefix .. "/96")
198
199                 -- v4 <-> siit route
200                 uci:delete_all("network", "route",
201                         function(s) return s.interface == "siit0" end)
202
203                 uci:section("network", "route", nil, {
204                         interface = "siit0",
205                         target    = gv4_net:network():string(),
206                         netmask   = gv4_net:mask():string()
207                 })
208
209         --
210         -- Client mode
211         --
212         --      * 172.23.2.1/24 on its lan, fdca:ffee:babe::1:2 on wl0 and the usual dummy address on siit0.
213         --      * we do a ::ffff:ffff:172.13.2.0/120 to siit0, because in this case, only traffic directed to clients needs to go into translation.
214         --      * same route as HNA6 announcement to catch the traffic out of the mesh.
215         --      * Also, MTU on LAN reduced to 1400.
216
217         else
218
219                 -- lan settings
220                 uci:tset("network", "lan", {
221                         mtu     = 1240,
222                         ipaddr  = lan_net:host():string(),
223                         netmask = lan_net:mask():string()
224                 })
225
226                 -- derive siit subnet from lan config
227                 siit_route = luci.ip.IPv6(
228                         siit_prefix .. "/" .. (96 + lan_net:prefix())
229                 ):add(lan_net[2])
230
231                 -- ipv4 <-> siit route
232                 uci:delete_all("network", "route",
233                         function(s) return s.interface == "siit0" end)
234
235                 -- XXX: kind of a catch all, gv4_net would be better
236                 --      but does not cover non-local v4 space
237                 uci:section("network", "route", nil, {
238                         interface = "siit0",
239                         target    = "0.0.0.0",
240                         netmask   = "0.0.0.0"
241                 })
242         end
243
244         -- setup the firewall
245         uci:delete_all("firewall", "zone",
246                 function(s) return (
247                         s['.name'] == "siit0" or s.name == "siit0" or
248                         s.network == "siit0" or s['.name'] == wifi_device or
249                         s.name == wifi_device or s.network == wifi_device
250                 ) end)
251
252         uci:delete_all("firewall", "forwarding",
253                 function(s) return (
254                         s.src == wifi_device and s.dest == "siit0" or
255                         s.dest == wifi_device and s.src == "siit0" or
256                         s.src == "lan" and s.dest == "siit0" or
257                         s.dest == "lan" and s.src == "siit0"
258                 ) end)
259
260         uci:section("firewall", "zone", "siit0", {
261                 name    = "siit0",
262                 network = "siit0",
263                 input   = "ACCEPT",
264                 output  = "ACCEPT",
265                 forward = "ACCEPT"
266         })
267
268         uci:section("firewall", "zone", wifi_device, {
269                 name    = wifi_device,
270                 network = wifi_device,
271                 input   = "ACCEPT",
272                 output  = "ACCEPT",
273                 forward = "ACCEPT"
274         })
275
276         uci:section("firewall", "forwarding", nil, {
277                 src  = wifi_device,
278                 dest = "siit0"
279         })
280
281         uci:section("firewall", "forwarding", nil, {
282                 src  = "siit0",
283                 dest = wifi_device
284         })
285
286         uci:section("firewall", "forwarding", nil, {
287                 src  = "lan",
288                 dest = "siit0"
289         })
290
291         uci:section("firewall", "forwarding", nil, {
292                 src  = "siit0",
293                 dest = "lan"
294         })
295
296         -- firewall include
297         uci:delete_all("firewall", "include",
298                 function(s) return s.path == "/etc/firewall.user" end)
299
300         uci:section("firewall", "include", nil, {
301                 path = "/etc/firewall.user"
302         })
303
304
305         -- siit0 interface
306         uci:delete_all("network", "interface",
307                 function(s) return ( s.ifname == "siit0" ) end)
308
309         uci:section("network", "interface", "siit0", {
310                 ifname  = "siit0",
311                 proto   = "none"
312         })
313
314         -- siit0 route
315         uci:delete_all("network", "route6",
316                 function(s) return siit_route:contains(luci.ip.IPv6(s.target)) end)
317
318         uci:section("network", "route6", nil, {
319                 interface = "siit0",
320                 target    = siit_route:string()
321         })
322
323         -- create wifi network interface
324         uci:section("network", "interface", wifi_device, {
325                 proto   = "static",
326                 mtu     = 1400,
327                 ip6addr = ula:string()
328         })
329
330         -- nuke old olsrd interfaces
331         uci:delete_all("olsrd", "Interface",
332                 function(s) return s.interface == wifi_device end)
333
334         -- configure olsrd interface
335         uci:foreach("olsrd", "olsrd",
336                 function(s) uci:set("olsrd", s['.name'], "IpVersion", 6) end)
337
338         uci:section("olsrd", "Interface", nil, {
339                 ignore      = 0,
340                 interface   = wifi_device,
341                 Ip6AddrType = "unique-local"
342         })
343
344         -- hna6
345         uci:delete_all("olsrd", "Hna6",
346                 function(s) return true end)
347
348         uci:section("olsrd", "Hna6", nil, {
349                 netaddr = siit_route:host():string(),
350                 prefix  = siit_route:prefix()
351         })
352
353         -- txtinfo v6 & olsrd nameservice
354         uci:foreach("olsrd", "LoadPlugin",
355                 function(s)
356                         if s.library == "olsrd_txtinfo.so.0.1" then
357                                 uci:set("olsrd", s['.name'], "accept", "::1")
358                         elseif s.library == "olsrd_nameservice.so.0.3" then
359                                 uci:set("olsrd", s['.name'], "name", hostname)
360                         end
361                 end)
362
363         -- lan dns
364         uci:tset("dhcp", "lan", {
365                 dhcp_option = "6," .. dns_server,
366                 start       = bit.band(lan_net:minhost():add(1)[2][2], 0xFF),
367                 limit       = ( 2 ^ ( 32 - lan_net:prefix() ) ) - 3
368         })
369
370         -- hostname
371         uci:foreach("system", "system",
372                 function(s)
373                         uci:set("system", s['.name'], "hostname", hostname)
374                 end)
375
376         uci:save("wireless")
377         uci:save("firewall")
378         uci:save("network")
379         uci:save("system")
380         uci:save("olsrd")
381         uci:save("dhcp")
382 end
383
384 return f