hoodselector 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. #!/usr/bin/lua
  2. -- This is the hoodselector. The hoodselector is one of the main components for
  3. -- splitting a layer 2 mesh network into seperated network segments (hoods).
  4. -- The job of the hoodselector is to automatically detect in which hood
  5. -- the router is located based on geo settings or by scanning its environment.
  6. -- Based on these informations the hoodselector should select a hood from a
  7. -- list of known hoods (hoodlist) and adjust vpn, wireless and mesh on lan
  8. -- configuration based on the settings given for the selected hood.
  9. --
  10. -- The hoodlist containing all hood settings is located in a seperate hoodfile
  11. -- in the hoods package.
  12. --
  13. -- The hoodselector depends on the folowing additional software:
  14. -- * fastd (vpn configuration) see getCurrentPeers(), setHoodVPN()
  15. -- * iw (wireless network scanning) see getNeigbourBssid()
  16. -- * batman-adv (mesh protocol) see directVPN(), getGwRange()
  17. -- * respondd (molwm) see molwm()
  18. --
  19. -- To detect the current hood the hoodselector knows 2 modes containing
  20. -- * 1. Default mode (VPN Router)
  21. -- - set real hood dependent on geo position.
  22. -- - set default hood dependent on geo position.
  23. -- * 2. Scan modes
  24. -- - Set wifi conf on scanned BSSID
  25. -- - Set vpn conf getting by BSSID (if no VPN conf exsist disable fastd)
  26. -- When selecting a hood, the hoodselector has the following priorities:
  27. -- 1. Selecting a hood by geo position depending on direct VPN connection.
  28. -- 2. force creating one mesh cloud with neigbour mesh routers
  29. -- 3. if routers had only mesh setting vpn config depends on the BSSID
  30. --
  31. -- Resources
  32. -- * https://wireless.wiki.kernel.org/en/users/documentation/iw
  33. -- MOLWM respondd file
  34. local molwmFile="/tmp/.hoodselector"
  35. local molwmtable = {}
  36. molwmtable["md5hash"] = ""
  37. molwmtable["vpnrouter"] = ""
  38. molwmtable["hoodname"] = ""
  39. -- PID file to ensure the hoodselector isn't running parallel
  40. local pidPath="/var/run/hoodselector.pid"
  41. if io.open(pidPath, "r") ~=nil then
  42. io.stderr:write("The hoodselector is still running.\n")
  43. os.exit(1)
  44. else
  45. io.close(io.open(pidPath, "w"))
  46. end
  47. local json = require ("luci.jsonc")
  48. local uci = require('luci.model.uci').cursor()
  49. local file = '/lib/ffnw/hoods/hoods.json'
  50. -- initialization done
  51. -- Read the full hoodfile. Return nil for wrong format or no such file
  52. local function readHoodfile(file)
  53. local jhood = io.open(file, 'r')
  54. if not jhood then return nil end
  55. local obj, pos, err = json.parse (jhood:read('*a'), 1, nil)
  56. if err then
  57. return nil
  58. else
  59. return obj
  60. end
  61. end
  62. local function mesh_on_wan_disable()
  63. os.execute('ifdown mesh_wan')
  64. io.stderr:write('Interface mesh_wan disabled.\n')
  65. end
  66. local function mesh_on_wan_enable()
  67. os.execute('ifup mesh_wan')
  68. io.stderr:write('Interface mesh_wan enabled.\n')
  69. end
  70. local function mesh_on_lan_disable()
  71. os.execute('ifdown mesh_lan')
  72. io.stderr:write('Interface mesh_lan disabled.\n')
  73. end
  74. local function mesh_on_lan_enable()
  75. os.execute('ifup mesh_lan')
  76. io.stderr:write('Interface mesh_lan enabled.\n')
  77. end
  78. local function molwm()
  79. local mesh_en = true
  80. local respondd = string.format("gluon-neighbour-info -i bat0 -p 1001 -d ff02::2 -r hoodselector -t 0.5")
  81. for line in io.popen(respondd, 'r'):lines() do
  82. local obj, pos, err = json.parse (line, 1, nil)
  83. if err then
  84. io.stderr:write("json parse error!\n")
  85. mesh_en = false
  86. break
  87. else
  88. if obj["hoodinfo"] ~= nil then
  89. if not ( obj["hoodinfo"]["md5hash"] == molwmtable["md5hash"]:gsub('\"', '') ) then
  90. io.stderr:write("hashes are not equals!\n")
  91. mesh_en = false
  92. break
  93. end
  94. end
  95. end
  96. end
  97. if uci:get('network', 'mesh_wan') and not mesh_en then
  98. mesh_on_wan_disable()
  99. end
  100. if uci:get('network', 'mesh_lan') and not mesh_en then
  101. mesh_on_lan_disable()
  102. end
  103. if uci:get('network', 'mesh_wan') and mesh_en then
  104. mesh_on_wan_enable()
  105. end
  106. if uci:get('network', 'mesh_lan') and mesh_en then
  107. mesh_on_lan_enable()
  108. end
  109. end
  110. -- Create md5 hash from currend hood
  111. local function molwm_md5hash(hood)
  112. local file = io.open("/tmp/.hoodhash", "w")
  113. if not file then
  114. io.stderr:write('\"/tmp/.hoodhash\" can not created\n')
  115. else
  116. file:write(json.stringify(hood, { indent = true }))
  117. file:close()
  118. --part to create md5 hash of this file
  119. for line in io.popen(string.format( "md5sum /tmp/.hoodhash")):lines() do
  120. for i in string.gmatch(line, "%S+") do
  121. if (string.len(i) == 32) then
  122. molwmtable["md5hash"] = "\"" .. string.format(i) .. "\""
  123. break
  124. end
  125. end
  126. end
  127. os.remove("/tmp/.hoodhash")
  128. end
  129. end
  130. -- Write MOLWM content into file
  131. local function write_molwm(hood)
  132. if hood ~= nil then
  133. molwm_md5hash(hood)
  134. molwmtable["hoodname"] = "\"" .. hood["name"] .. "\""
  135. end
  136. molwm()
  137. local file = io.open(molwmFile, "w")
  138. if not file then
  139. io.stderr:write(molwmFile ..' not found or not createble!\n')
  140. else
  141. file:write("\"md5hash\": " .. molwmtable["md5hash"] .. "\n")
  142. file:write("\"vpnrouter\": " .. molwmtable["vpnrouter"] .. "\n")
  143. file:write("\"hoodname\": " .. molwmtable["hoodname"] .. "\n")
  144. file:close()
  145. end
  146. end
  147. -- Program terminating function including removing of PID file
  148. local function exit()
  149. if io.open(pidPath, "r") ~=nil then
  150. os.remove(pidPath)
  151. end
  152. os.exit(0)
  153. end
  154. local function trim(s)
  155. -- from PiL2 20.4
  156. return (s:gsub("^%s*(.-)%s*$", "%1"))
  157. end
  158. local function sleep(n)
  159. os.execute("sleep " .. tonumber(n))
  160. end
  161. local function brclient_restart()
  162. os.execute('ifconfig br-client down')
  163. os.execute('ifconfig br-client up')
  164. io.stderr:write('Interface br-client restarted.\n')
  165. end
  166. local function vpn_stop()
  167. os.execute('/etc/init.d/fastd stop')
  168. io.stderr:write('VPN stopped.\n')
  169. end
  170. local function vpn_start()
  171. os.execute('/etc/init.d/fastd start')
  172. io.stderr:write('VPN started.\n')
  173. brclient_restart()
  174. end
  175. local function vpn_disable()
  176. -- disable VPN if not already disabled
  177. os.execute('/etc/init.d/fastd disable')
  178. io.stderr:write('VPN disabled.\n')
  179. end
  180. local function vpn_enable()
  181. -- enable VPN if not already enabled
  182. os.execute('/etc/init.d/fastd enable')
  183. io.stderr:write('VPN enable.\n')
  184. end
  185. local function wireless_restart()
  186. os.execute('wifi')
  187. io.stderr:write('Wireless restarted.\n')
  188. end
  189. -- Get a list of wifi devices return an emty table for no divices
  190. local function getWifiDevices()
  191. local radios = {}
  192. uci:foreach('wireless', 'wifi-device',
  193. function(s)
  194. table.insert(radios, s['.name'])
  195. end
  196. )
  197. return radios
  198. end
  199. -- Scans for wireless networks and returns a two dimensional array containing
  200. -- wireless mesh neigbour networks and their properties.
  201. -- The array is sorted descending by signal strength (strongest signal
  202. -- first, usually the local signal of the wireless chip of the router)
  203. local function wlan_list_sorted(radios)
  204. local networks = {}
  205. for index, radio in ipairs(radios) do
  206. local ifname = uci:get('wireless', 'ibss_' .. radio, 'ifname')
  207. local ssid = uci:get('wireless', 'ibss_' .. radio, 'ssid')
  208. if (ifname ~= nil and ssid ~= nil) then
  209. local wireless_scan = string.format( "iw %s scan", ifname)
  210. local row = {}
  211. row["radio"] = radio
  212. -- loop through each line in the output of iw
  213. for wifiscan in io.popen(wireless_scan, 'r'):lines() do
  214. -- the following line matches a new network in the output of iw
  215. if wifiscan:match("BSS (%w+:%w+:%w+:%w+:%w+:%w+)") then
  216. if(row["bssid"] ~= nil and row["quality"] ~= nil
  217. and row["ssid"] == ssid) then
  218. table.insert(networks, row)
  219. row = {}
  220. row["radio"] = radio
  221. end
  222. end
  223. -- get ssid
  224. if wifiscan:match("SSID:") then
  225. row["ssid"] = wifiscan:split(":")
  226. row["ssid"] = row["ssid"][2]
  227. if(row["ssid"] ~= nil) then
  228. row["ssid"] = trim(row["ssid"])
  229. end
  230. end
  231. -- get frequency
  232. if wifiscan:match("freq:") then
  233. row["frequency"] = wifiscan:split(":")
  234. row["frequency"] = row["frequency"][2]
  235. if(row["frequency"] ~= nil) then
  236. row["frequency"] = trim(row["frequency"])
  237. end
  238. end
  239. -- get bssid
  240. if wifiscan:match("(%w+:%w+:%w+:%w+:%w+:%w+)") then
  241. row["bssid"] = wifiscan:match("(%w+:%w+:%w+:%w+:%w+:%w+)"):upper()
  242. end
  243. -- get signal strength
  244. if wifiscan:match("signal:") then
  245. row["quality"] = wifiscan:split(" ")
  246. row["quality"] = row["quality"][2]:split(".")
  247. if row["quality"][1]:match("-") then
  248. row["quality"] = row["quality"][1]:split("-")
  249. end
  250. row["quality"] = tonumber(row["quality"][2]:match("(%d%d)"))
  251. end
  252. end
  253. else
  254. io.stderr:write("wireless uci config broken! abort...\n")
  255. exit();
  256. end
  257. end
  258. table.sort(networks, function(a,b) return a["quality"] < b["quality"] end)
  259. return networks
  260. end
  261. -- this method removes the wireless network of the router itself
  262. -- from the wlan_list
  263. local function filter_my_wlan_network(wlan_list)
  264. local filtered_wlan_list = {}
  265. for n,wlan in pairs(wlan_list) do
  266. if(wlan.quality ~= 0) then
  267. table.insert(filtered_wlan_list, wlan)
  268. end
  269. end
  270. return filtered_wlan_list
  271. end
  272. local function filter_default_hood_wlan_networks(default_hood, wlan_list)
  273. local filtered_wlan_list = {}
  274. for n,wlan in pairs(wlan_list) do
  275. if(default_hood.bssid ~= wlan.bssid) then
  276. table.insert(filtered_wlan_list, wlan)
  277. end
  278. end
  279. return filtered_wlan_list
  280. end
  281. -- bool if direct VPN. The detection is realaise by searching the fastd network interface inside the originator table
  282. local function directVPN()
  283. -- escape special chars "[]-"
  284. for outgoingIF in io.open("/sys/kernel/debug/batman_adv/bat0/originators", 'r'):lines() do
  285. local vpnIface = uci:get('fastd', 'mesh_vpn_backbone', 'net')
  286. if not vpnIface then
  287. io.stderr:write("fastd uci config broken! abort...\n")
  288. exit()
  289. end
  290. if outgoingIF:match(string.gsub("%[ " .. vpnIface .. "%]","%_",'-'):gsub("%-", "%%-")) then
  291. molwmtable["vpnrouter"] = "\"true\""
  292. return true
  293. end
  294. end
  295. molwmtable["vpnrouter"] = "\"false\""
  296. return false
  297. end
  298. -- Retun a table of current peers from /etc/config/fastd
  299. local function getCurrentPeers()
  300. local configPeers = {}
  301. local err = uci:foreach('fastd', 'peer',
  302. function(s)
  303. if s['.name'] then
  304. for prefix,peer in pairs(s) do
  305. local tmpPeer = {}
  306. if prefix:match(".name") then
  307. if peer:match("mesh_vpn_backbone_peer_") then
  308. -- val tmpRemote does not need uci exception check because its already include by "uci:foreach"
  309. local tmpRemote = uci:get('fastd', peer, 'remote')
  310. tmpRemote = tmpRemote[1]:split(" ")
  311. local remote = {}
  312. remote['host'] = tmpRemote[1]
  313. remote[tmpRemote[2]] = tmpRemote[3]
  314. -- uci:get does not need uci exception check because its already include by "uci:foreach"
  315. tmpPeer['key'] = tostring(uci:get('fastd', peer, 'key'))
  316. tmpPeer['remote'] = remote
  317. configPeers[peer] = tmpPeer
  318. end
  319. end
  320. end
  321. end
  322. end
  323. )
  324. if not err then
  325. io.stderr:write("fastd uci config broken! abort...\n")
  326. exit()
  327. end
  328. return configPeers
  329. end
  330. -- Get Geoposition. Return nil for no position
  331. local function getGeolocation()
  332. local ret = {}
  333. table.insert(ret, tonumber(uci:get('gluon-node-info', uci:get_first('gluon-node-info', 'location'), 'latitude')))
  334. table.insert(ret, tonumber(uci:get('gluon-node-info', uci:get_first('gluon-node-info', 'location'), 'longitude')))
  335. return ret
  336. end
  337. -- Return hood from the hood file based on geo position or nil, no real hood could be determined
  338. local function getHoodByGeo(jhood,geo)
  339. for n, h in pairs(jhood) do
  340. for n, box in pairs(h.boxes) do
  341. if ( geo[1] >= box[1][1] and geo[1] < box[2][1] and geo[2] >= box[1][2] and geo[2] < box[2][2] ) then
  342. return h
  343. end
  344. end
  345. end
  346. return nil
  347. end
  348. -- This method checks if the VPN configuration needs to be rewritten from the
  349. -- hoodfile. Therefore the method performs 3 checks and returns false if all
  350. -- checks fail. If one of the checks results to true the method returns true:
  351. -- 1. Check if the local VPN configuratin has a server that does not exist
  352. -- in the hoodfile.
  353. -- 2. Check if a server that does exist in the local VPN configuration AND
  354. -- in the hoodfile has a configuration change.
  355. -- 3. Check if the hoodfile contains a server that does not exist in the
  356. -- local VPN configuration.
  357. local function vpn_reconfiguration_needed(hood_serverlist,local_serverlist)
  358. -- Checks 1. and 2.
  359. for local_server_config_name, local_server in pairs(local_serverlist) do
  360. local local_server_exists_in_hoodfile = false
  361. for hood_server_index,hood_server in pairs(hood_serverlist) do
  362. if (local_server_config_name == 'mesh_vpn_backbone_peer_'.. hood_server["host"]:split('.')[1]:gsub("%-", "%_")) then
  363. local_server_exists_in_hoodfile = true
  364. if ( local_server.key ~= hood_server['publickey'] ) then
  365. return true
  366. end
  367. if ( local_server.remote.host ~= '\"'..hood_server["host"]..'\"' ) then
  368. return true
  369. end
  370. if ( local_server.remote.port ~= hood_server['port'] ) then
  371. return true
  372. end
  373. end
  374. end
  375. if not(local_server_exists_in_hoodfile) then return true end
  376. end
  377. -- Check 3.
  378. for hood_server_index,hood_server in pairs(hood_serverlist) do
  379. local hood_server_exists_locally = false
  380. for local_server_config_name, local_server in pairs(local_serverlist) do
  381. if (local_server_config_name == 'mesh_vpn_backbone_peer_'.. hood_server["host"]:split('.')[1]:gsub("%-", "%_")) then
  382. hood_server_exists_locally = true
  383. end
  384. end
  385. if not(hood_server_exists_locally) then return true end
  386. end
  387. return false
  388. end
  389. -- Reconfigure fastd
  390. local function vpn_reconfigure(hood_serverlist,local_serverlist)
  391. -- remove all servers
  392. for config_index, local_server in pairs(local_serverlist) do
  393. uci:delete('fastd',config_index)
  394. end
  395. -- add servers from hoodfile
  396. local group = 'mesh_vpn_backbone'
  397. for i,hood_server in pairs(hood_serverlist) do
  398. uci:section('fastd', 'peer', group .. '_peer_' .. hood_server.host:split('.')[1]:gsub("%-", "%_"),
  399. {
  400. enabled = 1,
  401. net = 'mesh_vpn',
  402. group = group,
  403. key = hood_server.publickey,
  404. remote = {'\"'..hood_server.host..'\"'..' port '..hood_server.port}
  405. }
  406. )
  407. end
  408. uci:save('fastd')
  409. uci:commit('fastd')
  410. io.stderr:write('Fastd needed reconfiguration. Stopped and applied new settings.\n')
  411. end
  412. -- Checks if wireless needs a reconfiguration. Returns true if any of the checks
  413. -- passes. Otherwise the method returns false.
  414. local function wireless_reconfiguration_needed(radios, hood_bssid)
  415. for index, radio in ipairs(radios) do
  416. if ( uci:get('wireless', 'ibss_' .. radio, 'bssid') ~= hood_bssid ) then
  417. return true
  418. end
  419. end
  420. return false
  421. end
  422. -- Reconfigure wireless
  423. local function wireless_reconfigure(radios, hood_bssid)
  424. for index, radio in ipairs(radios) do
  425. if not ( uci:get('wireless', 'ibss_' .. radio, 'bssid') == hood_bssid ) then
  426. uci:section('wireless', 'wifi-iface', 'ibss_' .. radio, {
  427. bssid = hood_bssid
  428. })
  429. end
  430. end
  431. uci:save('wireless')
  432. uci:commit('wireless')
  433. end
  434. -- This method sets a new hoodconfig and takes care that services are only
  435. -- stopped or restarted if reconfiguration is needed.
  436. -- Process:
  437. -- * Check if wireless needs reconfiguration and prepare reconfiguration
  438. -- * Check if fastd needs reconfiguration and prepare reconfiguration
  439. -- * If fastd needs reconfiguration, stop fastd and apply new settings but
  440. -- dont restart it before wireless has been reconfigured
  441. -- * If wireless needs reconfiguration apply new settings and restart wireless
  442. -- * If fastd needed reconfiguration start fastd now
  443. local function set_hoodconfig(hood, radios)
  444. local local_serverlist = getCurrentPeers()
  445. -- Check if VPN needs reconfiguration because in case of reconfiguration we
  446. -- need to stop VPN before we can reconfigure any other connection.
  447. local vpn_reconfiguration_needed = vpn_reconfiguration_needed(hood["servers"],local_serverlist);
  448. if(vpn_reconfiguration_needed) then
  449. vpn_stop()
  450. end
  451. -- reconfigure wireless
  452. if(wireless_reconfiguration_needed(radios, hood["bssid"])) then
  453. wireless_reconfigure(radios, hood["bssid"])
  454. wireless_restart()
  455. io.stderr:write('Wireless needed reconfiguration. Applied new settings and restarted.\n')
  456. end
  457. -- reconfigure fastd
  458. if (vpn_reconfiguration_needed) then
  459. vpn_reconfigure(hood["servers"],local_serverlist)
  460. -- scan mode can disable VPN so we need to make shure that VPN is enabled
  461. -- if the router selects a hood
  462. vpn_enable()
  463. vpn_start()
  464. io.stderr:write('VPN needed reconfiguration. Applied new settings and restarted.\n')
  465. end
  466. io.stderr:write("Set hood \""..hood["name"].."\"\n")
  467. molwmtable["hoodname"] = "\"" .. hood["name"] .. "\""
  468. return true
  469. end
  470. -- Return the default hood in the hood list.
  471. -- This method can return the following data:
  472. -- * default hood
  473. -- * nil if no default hood has been defined
  474. local function getDefaultHood(jhood)
  475. for n, h in pairs(jhood) do
  476. if h.defaulthood then
  477. return h
  478. end
  479. end
  480. return nil
  481. end
  482. -- boolean check if batman-adv has gateways
  483. local function batmanHasGateway()
  484. for gw in io.open("/sys/kernel/debug/batman_adv/bat0/gateways", 'r'):lines() do
  485. if gw:match("Bit") then
  486. return true
  487. end
  488. end
  489. return false
  490. end
  491. -- Return hood from the hood file based on a given BSSID. nil if no matching hood could be found
  492. local function gethoodByBssid(jhood, scan_bssid)
  493. for n, h in pairs(jhood) do
  494. if scan_bssid:match(h.bssid) then
  495. return h
  496. end
  497. end
  498. return nil
  499. end
  500. -- Return hood from hood file based on a peer address. nil if no matching hood could be found
  501. local function getCurrentHood(jhood)
  502. for local_server_config_name, local_server in pairs(getCurrentPeers()) do
  503. for n, h in pairs(jhood) do
  504. for n, peer in pairs(h.servers) do
  505. if ( peer["host"] == local_server.remote.host:gsub("\"", "") ) then
  506. return h
  507. end
  508. end
  509. end
  510. end
  511. return nil
  512. end
  513. local function get_batman_mesh_network(sorted_wlan_list, defaultHood)
  514. io.stderr:write('Testing neighboring adhoc networks for batman advanced gw connection.\n')
  515. io.stderr:write('The following wireless networks have been found:\n')
  516. for n, network in pairs(sorted_wlan_list) do
  517. print(network["quality"].."\t"..network["frequency"].."\t"..network["bssid"].."\t"..network["ssid"])
  518. end
  519. -- we dont want to get tricked by our signal
  520. sorted_wlan_list = filter_my_wlan_network(sorted_wlan_list)
  521. -- we dont want to test the default hood because if there is no other
  522. -- hood present we will connect to the default hood anyway
  523. sorted_wlan_list = filter_default_hood_wlan_networks(defaultHood, sorted_wlan_list)
  524. io.stderr:write('After filtering we will test the following wireless networks:\n')
  525. for n, network in pairs(sorted_wlan_list) do
  526. print(network["quality"].."\t"..network["frequency"].."\t"..network["bssid"].."\t"..network["ssid"])
  527. end
  528. local bssid = nil
  529. if(next(sorted_wlan_list)) then
  530. io.stderr:write("Prepare configuration for testing wireless networks...\n")
  531. -- Notice:
  532. -- we will use iw for testing the wireless networks because using iw does
  533. -- not need any changes inside the uci config. This approach allows the
  534. -- router to automatically reset to previous configuration in case
  535. -- someone disconnects the router from power during test.
  536. -- stop vpn to prevent two hoods from beeing connected in case
  537. -- the router gets internet unexpectedly during test.
  538. vpn_stop()
  539. -- remove the ap network because we cannot change
  540. -- the settings of the adhoc network if the ap network is still operating
  541. os.execute("iw dev client0 del")
  542. for n, wireless in pairs(sorted_wlan_list) do
  543. io.stderr:write("Testing "..wireless["bssid"].."...")
  544. -- leave the current adhoc network
  545. os.execute("iw dev ibss0 ibss leave")
  546. -- setup the adhoc network we want to test
  547. os.execute("iw dev ibss0 ibss join "..wireless["ssid"].." "..wireless["frequency"].." "..wireless["bssid"])
  548. -- sleep 30 seconds till the connection is fully setup
  549. sleep(30)
  550. if batmanHasGateway() then
  551. bssid = wireless["bssid"]
  552. break;
  553. end
  554. end
  555. vpn_start()
  556. wireless_restart()
  557. io.stderr:write("Finished testing wireless networks, restored previous configuration\n")
  558. end
  559. return bssid
  560. end
  561. -- INITIALIZE AND PREPARE DATA --
  562. -- read hoodfile, exit if reading the hoodfile fails
  563. local jhood = readHoodfile(file)
  564. if jhood == nil then
  565. io.stderr:write('There seems to have gone something wrong while reading hoodfile from ' .. file .. '\n')
  566. exit()
  567. end
  568. -- check if a default hood has been defined and exit if none has been defined
  569. local defaultHood = getDefaultHood(jhood)
  570. if defaultHood == nil then
  571. io.stderr:write('No defaulthood defined.\n')
  572. exit()
  573. end
  574. -- Get list of wifi devices
  575. local radios = getWifiDevices()
  576. -- VPN MODE
  577. -- If we have a VPN connection then we will try to get the routers location and
  578. -- select the hood coresponding to our location.
  579. -- If no hood for the location has been defined, we will select
  580. -- the default hood.
  581. -- If we can not get our routers location, we will fallback to scan mode.
  582. if directVPN() then
  583. io.stderr:write('VPN connection found.\n')
  584. local geo = getGeolocation()
  585. if geo[1] ~= nil and geo[2] ~= nil then
  586. io.stderr:write('Position found.\n')
  587. local geoHood = getHoodByGeo(jhood, geo)
  588. if geoHood ~= nil then
  589. set_hoodconfig(geoHood, radios)
  590. io.stderr:write('Hood set by VPN mode.\n')
  591. write_molwm(geoHood)
  592. exit()
  593. end
  594. io.stderr:write('No hood has been defined for current position.\n')
  595. set_hoodconfig(defaultHood, radios)
  596. io.stderr:write('Defaulthood set.\n')
  597. write_molwm(defaultHood)
  598. exit()
  599. end
  600. io.stderr:write('No position found\n')
  601. else
  602. io.stderr:write('No VPN connection found\n')
  603. end
  604. if batmanHasGateway() then
  605. io.stderr:write('Batman gateways found, everything seems to be ok - doing nothing\n')
  606. local currendHood = getCurrentHood(jhood)
  607. if currendHood ~= nil then
  608. write_molwm(currendHood)
  609. end
  610. exit()
  611. end
  612. -- SCAN MODE
  613. if next(radios) then
  614. -- check if there exist a neighboring freifunk batman advanced mesh
  615. -- network with an active connection to a batman advanced gateway
  616. local sortedWlanList = wlan_list_sorted(radios)
  617. local meshBSSID = get_batman_mesh_network(sortedWlanList, defaultHood)
  618. if meshBSSID ~= nil then
  619. io.stderr:write("Neighoring freifunk batman advanced mesh with BSSID "..meshBSSID.." found\n")
  620. local bssidHood = gethoodByBssid(jhood, meshBSSID)
  621. if bssidHood ~= nil then
  622. set_hoodconfig(bssidHood, radios)
  623. io.stderr:write('Hood set by scan mode\n')
  624. write_molwm(bssidHood)
  625. exit()
  626. end
  627. -- if the bssid does not corespond to any hood, we disable vpn and
  628. -- just establish a wireless connection to the mesh without any vpn or
  629. -- mesh on lan (TODO) connectivity
  630. vpn_stop()
  631. vpn_disable()
  632. wireless_reconfigure(radios, meshBSSID)
  633. wireless_restart()
  634. io.stderr:write('Could not select a hood but established a connection via wireless mesh.\n')
  635. io.stderr:write('Disabled all connections except connections via wireless mesh.\n')
  636. local currendHood = getCurrentHood(jhood)
  637. if currendHood ~= nil then
  638. write_molwm(currendHood)
  639. end
  640. exit()
  641. end
  642. io.stderr:write('No neighboring freifunk batman advanced mesh found.\n')
  643. end
  644. -- DEFAULT-HOOD MODE
  645. -- If we do NOT have a VPN connection AND found no freifunk mesh network while
  646. -- scanning then we set the default hood
  647. set_hoodconfig(defaultHood, radios)
  648. io.stderr:write('Set defaulthood.\n')
  649. write_molwm(defaultHood)
  650. exit()