Developing lightweight computation at the DSG edge

easy_setup.lua 18.5 KB
Newer Older
1
--[[
2
3
  qMp - Quick Mesh Project - https://www.qmp.cat
  Copyright © 2011-2017 Fundació Privada per a la Xarxa Oberta, Lliure i Neutral, guifi.net
4

5
6
7
8
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
9

10
11
12
13
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
14

15
16
  You should have received a copy of the GNU General Public License
  along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18
19
20
21
22
23
--]]

local sys = require "luci.sys"
local http = require "luci.http"
local ip = require "luci.ip"
local util = require "luci.util"
local uci  = require "luci.model.uci"
24
local uciout = luci.model.uci.cursor()
25
26
27
28

package.path = package.path .. ";/etc/qmp/?.lua"
qmpinfo = require "qmpinfo"

29
30
31
32
33
34
35
36
local wdevs = qmpinfo.get_wifi_index()

for _,wdev in ipairs(wdevs) do
  mydev = uci:get("qmp",wdev,"device")
end

local iw = luci.sys.wifi.getiwinfo(mydev)

37
38
39
40
------------
-- Header --
------------
m = SimpleForm("qmp", translate("qMp easy setup"), translate("This page provides a fast and simple way to configure the basic settings of a qMp device.") .. " " .. translate("Use the form below to specify the required settings, such as the node mode, name or identifier, IP address and interface operation modes.") .. "<br/> <br/>" .. translate("You can check the on-line documentation at <a href=\"https://www.qmp.cat/Web_interface\">https://www.qmp.cat/Web_interface</a> for more information about the different options."))
41

42
43
44
---------------------------
-- Device identification --
---------------------------
45

46
47
48
49
-- Device name
local devicename_help
local devicename_help = m:field(DummyValue,"_devicename_help")
devicename_help.rawhtml = true
50
devicename_help.default = "<legend>"..translate("Device identification").."</legend>".."<br/> <br/>"..translate("Choose a name and a numeric ID for this device. It will be used to identify it in the mesh network:").."<br/> <br/>"
51

52
local devicename = m:field(Value, "_devicename", translate("Device name"), translate("Use only alphanumeric characters, dots, dashes and underscores."))
53
54
55
devicename.datatype="hostname"
devicename.optional=false
devicename.default="MyMeshDevice"
56

57
if uciout:get("qmp","node","device_name") ~= nil then
58
59
60
  devicename.default = uciout:get("qmp","node","device_name")
else
  devicename.default = "qMp"
61
end
62

63
64
65
66
67
68
69
70
71
72
73
-- Device ID device
local deviceid = m:field(Value, "_deviceid", translate("Device ID"), translate("Use hexadecimal characters only."))
deviceid:depends({_communityname = "Guifi.net"})
deviceid.datatype="string"
deviceid.optional = false

if uciout:get("qmp","node","device_id") ~= nil then
  deviceid.default = uciout:get("qmp","node","device_id")
else
  deviceid.default = "0000"
end
74

75
-- Community network name
76
77
78
local communityname_help
communityname_help = m:field(DummyValue,"_communityname_help")
communityname_help.rawhtml = true
79
communityname_help.default = translate("If the device is part of a community network, select it:") .. "<br/> <br/>"
80

81
local communityname = m:field(Value, "_communityname", " ", translate("Select a predefined community network from the list, type your own name or leave it blank."))
82
83
84
85
communityname.datatype="string"
communityname:value("Bogotá Mesh","Bogotá Mesh")
communityname:value("DigitalMerthyr","Digital Merthyr")
communityname:value("Guifi.net","Guifi.net")
86
communityname:value("NYCMesh","NYC Mesh")
87
88

if uciout:get("qmp","node","community_name") ~= nil then
89
  communityname.default=uciout:get("qmp","node","community_name")
90
91
92
end


93
94
-- Mesh network name
local guifimeshname = m:field(Value, "_guifimeshname", " ", translate("Select a predefined mesh network, type your own name or leave it blank."))
95
96
97
guifimeshname:depends("_communityname","Guifi.net")
guifimeshname.datatype="string"
guifimeshname.default="GuifiSants"
98
99
guifimeshname:value("GuifiBaix", "Baix Llobregat (GuifiBaix)")
guifimeshname:value("Bellvitge", "Bellvitge (HW)")
100
101
102
guifimeshname:value("GraciaSenseFils", "Gràcia Sense Fils (GSF)")
guifimeshname:value("PoblenouSenseFils", "Poblenou Sense Fils (P9SF)")
guifimeshname:value("Quesa", "Quesa (QUESA)")
Roger Pueyo Centelles's avatar
Roger Pueyo Centelles committed
103
guifimeshname:value("Raval", "Raval (RAV)")
104
105
106
guifimeshname:value("GuifiSants", "Sants-Les Corts-UPC (GS)")
guifimeshname:value("SantAndreu", "Sant Andreu (SAND)")
guifimeshname:value("Vallcarca", "Vallcarca (VKK)")
107
108
guifimeshname:value("Herguijuela", "La Herguijuela (LHer)")
guifimeshname:value("CepedaLaMora", "Cepeda la Mora (CPD)")
109
110

if uciout:get("qmp","node","mesh_name") ~= nil then
111
  guifimeshname.default=uciout:get("qmp","node","mesh_name")
112
113
114
end


115
116
117
118
----------------------------------------
-- Node mode and public IPv4 address  --
----------------------------------------

119
120
121
122
-- Node mode
local mode_help
mode_help = m:field(DummyValue,"mode_help")
mode_help.rawhtml = true
123
mode_help.default = "<legend>" .. translate("Node mode and mesh-wide public IPv4 address") .. "</legend>" .. "<br/> <br/>" ..
124
125
126
127
128
  translate("The <em>node mode</em> option defines whether qMp makes the devices connected to the LAN interfaces of the node visible to the rest of the mesh network or hidden behind a NAT.") .. " " ..
  translate("Static, long-term deployments such as <em>community networks</em> usually choose <em>public</em> mode, whereas quick, temporal or ephemeral deployments usually choose <em>natted</em> mode.") .. "<br/> <br/>" ..
  translate("Choose an operating mode for this node:") .. "<br/> <br/>" ..
  translate("· <em>public</em> mode, for making local devices connected to this node accessible from anywhere in the mesh network") .. "<br/>" ..
  translate("· <em>natted</em> mode, for keeping local devices connected to this node hidden from the rest of the mesh by a NAT") .. "<br/> <br/>"
129

130
nodemode = m:field(ListValue, "_nodemode", translate(" "), translate("Select <em>public</em> or <em>natted</em> mode."))
131
132
133
134
135
136
nodemode:value("community","public")
nodemode:value("roaming","natted")

local networkmode
if uciout:get("qmp","roaming","ignore") == "1" then
  local ipv4 = uciout:get("qmp","networks","bmx6_ipv4_address")
137
138
139
  if ipv4 ~= nil then
   local ipv4mask = string.find(ipv4,"/")
  end
140
141
142
  if ipv4mask ~= nil then
    ipv4 = string.sub(ipv4,1,ipv4mask-1)
  end
143
144
  -- This caused issue #465 and doesn't make much sense here... (ToDo: investigate further)
  -- if ipv4 == uciout:get("qmp","networks","lan_address") then
145
    networkmode="community"
146
  -- end
147
148
149
150
151
152
153
else
  networkmode="roaming"
end
nodemode.default=networkmode


-- Mesh IPv4 address (natted)
154
155
156
local roaming_ipaddress_help
roaming_ipaddress_help = m:field(DummyValue,"roaming_ipaddress_help")
roaming_ipaddress_help.rawhtml = true
157
roaming_ipaddress_help:depends("_nodemode","roaming")
158
roaming_ipaddress_help.default = translate("In <em>natted</em> mode, all qMp devices in the mesh network need a unique IPv4 address with a /32 netmask.") .. " " ..
159
  translate("If unsure about which one to select, leave the field blank and a random one will be assigned automatically.").."<br/> <br/>"
160

161
local nodeip_roaming =  m:field(Value, "_nodeip_roaming", " ", translate("Write the mesh-wide public IPv4 address for this device with a /32 netmask, or leave it blank to get a random one."))
162
nodeip_roaming:depends("_nodemode","roaming")
163
nodeip_roaming.datatype="cidr4"
164
165
166

local rip = uciout:get("qmp","networks","bmx6_ipv4_address")
if rip == nil or #rip < 7 then
167
168
169
170
  rip = uciout:get("bmx6","general","tun4Address")
  if rip == nil or #rip < 7 then
    rip = ""
  end
171
end
172
173

nodeip_roaming.default=rip
174

175

176
-- Mesh IPv4 address (public)
177
178
179
local community_addressing_help
community_addressing_help = m:field(DummyValue,"community_addressing_help")
community_addressing_help.rawhtml = true
180
community_addressing_help:depends("_nodemode","community")
181
community_addressing_help.default = "<strong>" .. " " .. "</strong>" .. "<br/> <br/>" ..
182
183
184
  translate("In <em>public</em> mode, all qMp devices in the mesh network need a unique IPv4 address and a subnetwork mask.") .. " " ..
  translate("Specify the IP address and the subnetwork mask for this device, according to the planning of your community network or deployment.") .. " " ..
  translate("End-user devices will get an IPv4 address within the valid range determined by these two values.").."<br/> <br/>"
185

186
local nodeip = m:field(Value, "_nodeip", " ", translate("Main IPv4 address for this device."))
187
nodeip:depends("_nodemode","community")
188
nodeip.optional=false
189
190
nodeip.datatype="ip4addr"

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
local pip = uciout:get("qmp","networks","bmx6_ipv4_address")
if pip == nil or #pip < 7 then
  pip = uciout:get("bmx6","general","tun4Address")
  if pip == nil or #pip < 7 then
    pip = "10.30."..util.trim(util.exec("echo $((($(date +%M)*$(date +%S)%254)+1))"))..".1"
  end
end

if string.find(pip, "/") then
  pip = string.sub(pip, 0, string.find(pip, "/") -1)
end

nodeip.default=pip





209
210
-- Mesh IPv4 netmask (public)
local nodemask = m:field(Value, "_nodemask"," ", translate("Network mask to be used with the IPv4 address above."))
211
nodemask:depends("_nodemode","community")
212
213
214
215
216
217
218
nodemask.default = "255.255.255.224"
nodemask:value("255.255.255.0", "255.255.255.0 (/24, 254 hosts)")
nodemask:value("255.255.255.128", "255.255.255.128 (/25, 126 hosts)")
nodemask:value("255.255.255.192", "255.255.255.192 (/26, 62 hosts)")
nodemask:value("255.255.255.224", "255.255.255.224 (/27, 30 hosts)")
nodemask:value("255.255.255.240", "255.255.255.240 (/28, 14 hosts)")
nodemask:value("255.255.255.248", "255.255.255.248 (/29, 6 hosts)")
219
220
221
nodemask.datatype="ip4addr"

if networkmode == "community" then
222
223
  nodeip.default=uciout:get("qmp","networks","lan_address")
  nodemask.default=uciout:get("qmp","networks","lan_netmask")
224
225
end

226

227
228
229
230
------------------------
-- Network interfaces --
------------------------

231
-- Wired interfaces
232
233
234
local wired_interface_mode_help
wired_interface_mode_help = m:field(DummyValue,"wired_interface_mode_help")
wired_interface_mode_help.rawhtml = true
235
wired_interface_mode_help.default = "<legend>" .. translate("Wired network interfaces") .. "</legend>" .. "<br/> <br/>" ..
236
237
238
239
240
  translate("Select the working mode of the wired network interfaces") .. ":<br/> <br/>" ..
  translate("· <em>LAN</em> mode is used to provide connectivity to end-users (a DHCP server will be enabled to assign IP addresses to the devices connecting)") .. "<br/>" ..
  translate(" · <em>WAN</em> mode is used on interfaces connected to an Internet up-link or any other gateway connection") .. "<br/>" ..
  translate(" · <em>None</em>, to not use the interface neither as LAN nor as WAN") .. "<br/> <br/>" ..
  translate(" · <em>Mesh via wired interface</em> is used to expand the mesh network when connecting the wired interface to other qMp devices") .. "<br/> <br/>"
241

242
243
244
245
246
-- Get list of devices {{ethernet}{wireless}}
devices = qmpinfo.get_devices()

-- Ethernet devices
nodedevs_eth = {}
247
nodedevs_ethmesh = {}
248
249

local function is_a(dev, what)
250
251
  local x
  for x in util.imatch(uciout:get("qmp", "interfaces", what)) do
252
253
254
255
    if dev == x then
      return true
    end
  end
256
  return false
257
258
259
end

for i,v in ipairs(devices.eth) do
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
  emode = m:field(ListValue, "_" .. v, translatef("Wired interface <strong>%s</strong>",v))
  emode:value("Lan", translate("LAN"))
  emode:value("Wan", translate("WAN"))
  emode:value("none", translate("None"))

  if is_a(v, "lan_devices") then
    emode.default = "Lan"
  elseif is_a(v, "wan_devices") then
    emode.default = "Wan"
  else
    emode.default = "none"
  end

  emeshmode = m:field(ListValue, "_" .. v.."mesh", translatef("Mesh via <strong>%s</strong>",v))
  emeshmode:value("Mesh", translate("Yes"))
  emeshmode:value("none", translate("No"))

  if is_a(v, "mesh_devices") then
    emeshmode.default = "Mesh"
  else
    emeshmode.default = "none"
  end

  nodedevs_eth[i] = {v,emode}
  nodedevs_ethmesh[i] = {v,emeshmode}
285
286
end

287

288
-- Wireless interfaces
289
290
291
local wireless_interface_mode_help
wireless_interface_mode_help = m:field(DummyValue,"wireless_interface_mode_help")
wireless_interface_mode_help.rawhtml = true
292
293
294
295
wireless_interface_mode_help.default = "<legend>" .. translate("Wireless network interfaces") .. "</legend>" .. "<br/> <br/>" ..
  translate("Select the working mode of the wireless network interfaces") .. ":<br/> <br/>" ..
  translate("· <em>802.11s (mesh)</em> mode is used to link with other mesh nodes operating in <strong>802.11s mesh</strong> mode") .."<br/>" ..
  translate("· <em>802.11s (mesh) + Ad hoc (legacy mesh)</em> mode is used to link with other mesh nodes operating in <strong>802.11s mesh</strong> or in <strong>legacy ad hoc mesh</strong> mode. Use this one for <strong>backwards compatibility</strong> with old qMp deployments.") .."<br/>" ..
296
  translate("· <em>Ad hoc (legacy mesh)</em> mode is used to link with other mesh nodes operating in <strong>legacy ad hoc mesh</strong> mode") .. "<br/>" ..
297
298
299
300
301
302
  translate("· <em>AP (mesh)</em> mode is used to create an access point for other mesh nodes to connect as clients") .. "<br/>" ..
  translate("· <em>Client (mesh)</em> mode is used to link with a mesh node operating in AP mode") .. "<br/>" ..
  translate("· <em>AP (LAN)</em> mode is used to generate an access point for end users' devices") .. "<br/>" ..
  translate("· <em>Client (WAN)</em> mode is used to link work as a client of an access point providing an up-link Internet access") .. "<br/>"..
  translate("· <em>Ad hoc (mesh) + AP (LAN)</em> combines both modes on a single interface") .. "<br/>" ..
  translate("· <em>802.11s (mesh) + AP (LAN)</strong> combines both modes on a single interface").."<br/> <br/>"
303

304
nodedevs_wifi = {}
305

306
for i,v in ipairs(devices.wifi) do
307
308
  wmode = m:field(ListValue, "_"..v.."_mode", translatef("Wireless interface <strong>%s</strong>",v))
  wmode:value("80211s","802.11s (mesh)")
309
310
  wmode:value("80211s_adhoc","802.11s (mesh) + Ad hoc (legacy mesh)")
  wmode:value("adhoc","Ad hoc (legacy mesh)")
311
312
313
314
315
316
317
  wmode:value("ap","Access point (mesh)")
  wmode:value("client","Client (mesh)")
  wmode:value("aplan","Access point (LAN)")
  wmode:value("clientwan","Client (WAN)")
  wmode:value("adhoc_ap","Ad hoc (mesh) + access point (LAN)")
  wmode:value("80211s_aplan","802.11s (mesh) + access point (LAN)")
  wmode:value("none","Disabled")
318
  wmode.default = "80211s_aplan"
319
320
321
322
323
324
325
326

  wchan = m:field(ListValue, "_".. v.."_chan", translate("Channel"))
  for _,ch in ipairs(qmpinfo.get_channels(v)) do
    wchan:value(ch.channel, ch.channel)
    if ch.ht40p then wchan:value(ch.channel .. '+', ch.channel .. '+') end
    if ch.ht40m then wchan:value(ch.channel .. '-', ch.channel .. '-') end
    if ch.channel < 15 then wchan:value(ch.channel .. 'b', ch.channel .. 'b') end
  end
327

328
329
330
331
332
  -- Check if the device is adhoc_ap mode, then Mode=AP MeshAll=1
  uciout:foreach("qmp","wireless", function (s)
    if s.device == v then
      if s.mode ~= nil then
        wmode.default = s.mode
333
        wchan.default = s.channel
334
335
336
      end
    end
  end)
337

338
  nodedevs_wifi[i] = {v,wmode,wchan}
339
340
end

341
342
343
344
345
346
347
348
349
350
351
352
353
354
-- Country selection (global, not per-device)
local cl = iw and iw.countrylist

if cl and #cl > 0 then
  wcountry = m:field(ListValue, "country", translate("Country"))

  wcountry.default = uciout:get("qmp","wireless","country")
  for _, s in ipairs(cl) do
    wcountry:value(s.alpha2, "%s - %s" %{ s.alpha2, s.name })
  end

else
  wcountry = m:field(Value, "country", translate("Country Code"), translate("Use ISO/IEC 3166 alpha2 country codes."))
end
355

356

357
function nodemode.write(self, section, value)
358
359
360
361
362
363
364
365
366
  local device_name = devicename:formvalue(section)
  local mode = nodemode:formvalue(section)
  local nodeip = nodeip:formvalue(section)
  local nodemask = nodemask:formvalue(section)
  local nodeip_roaming = nodeip_roaming:formvalue(section)

  local community_name = communityname:formvalue(section)
  if community_name == "Guifi.net" then
    local mesh_name = guifimeshname:formvalue(section)
367
    local device_id = deviceid:formvalue(section)
368
369
370
    uciout:set("qmp","node","mesh_name",mesh_name)
    uciout:set("qmp","node","device_id",device_id)
  end
371
372
  uciout:set("qmp","node","community_name",community_name)

373
  if mode == "community" then
374
375
376
377
378
379
380
381
382
383
384
385
386
387
    uciout:set("qmp","roaming","ignore","1")
    uciout:set("qmp","networks","publish_lan","1")
    uciout:set("qmp","networks","lan_address",nodeip)
    uciout:set("qmp","networks","bmx6_ipv4_address",ip.IPv4(nodeip,nodemask):string())
    uciout:set("qmp","networks","lan_netmask",nodemask)
    uciout:set("qmp","node","device_name",device_name)
    uciout:set("qmp","node","community_name",community_name)

  else
    uciout:set("qmp","roaming","ignore","0")
    uciout:set("qmp","networks","publish_lan","0")
    uciout:set("qmp","networks","lan_address","172.30.22.1")
    uciout:set("qmp","networks","lan_netmask","255.255.0.0")
    uciout:set("qmp","networks","bmx6_ipv4_prefix24","10.202.0")
Roger Pueyo Centelles's avatar
Roger Pueyo Centelles committed
388
    uciout:set("qmp","node","device_name",device_name)
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
    if nodeip_roaming == nil then
      uciout:set("qmp","networks","bmx6_ipv4_address","")
    else
      uciout:set("qmp","networks","bmx6_ipv4_address",nodeip_roaming)
    end

  end

  local i,v,devmode,devname
  local lan_devices = ""
  local wan_devices = ""
  local mesh_devices = ""
  local meshall = "1"

  for i,v in ipairs(nodedevs_eth) do
    devname = v[1]
405
    devmode = v[2]:formvalue(section)
406

407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
    if devmode == "Lan" then
      lan_devices = lan_devices..devname.." "
    elseif devmode == "Wan" then
      wan_devices = wan_devices..devname.." "
    end
  end

  for i,v in ipairs(nodedevs_ethmesh) do
    devname = v[1]
    devmode = v[2]:formvalue(section)

    if devmode == "Mesh" then
      mesh_devices = mesh_devices..devname.." "
    end
  end
422

423
  for i,v in ipairs(nodedevs_wifi) do
424
    devname = v[1]
425
426
427
    devmode = v[2]:formvalue(section)
    devchan = v[3]:formvalue(section)

428

429
430
431
432
433
    if (devmode == "AP" and meshall == "1") or devmode == "Mesh" then
      mesh_devices = mesh_devices..devname.." "
    elseif devmode == "AP" and meshall ~= "1" then
      lan_devices = lan_devices..devname.." "
    end
434

435
436
437
    function setwmode(s)
      if s.device == devname then
        uciout:set("qmp",s['.name'],"mode",devmode)
438
        uciout:set("qmp",s['.name'],"channel",devchan)
439
440
441
      end
    end
    uciout:foreach("qmp","wireless",setwmode)
442
  end
443

444
445
446
  uciout:set("qmp","interfaces","lan_devices",lan_devices)
  uciout:set("qmp","interfaces","wan_devices",wan_devices)
  uciout:set("qmp","interfaces","mesh_devices",mesh_devices)
447

448
449
450
  local country = wcountry:formvalue(section)
  uciout:set("qmp", "wireless", "country", country)

451
452
  uciout:commit("qmp")
  apply()
453
454
455
end

function apply(self)
456
  http.redirect("/luci-static/resources/qmp/wait_long.html")
457
  luci.sys.call('(qmpcontrol configure_wifi ; qmpcontrol configure_network) &')
458
459
460
461
end


return m