gluon-hoodselector: add VPN-state and checksite
Signed-off-by: Jan-Tarek Butt <tarek@ring0.de>
This commit is contained in:
parent
5bf160040e
commit
95566316cb
30
package/gluon-hoodselector/Makefile
Normal file
30
package/gluon-hoodselector/Makefile
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=gluon-hoodselector
|
||||||
|
|
||||||
|
GLUON_VERSION = 3
|
||||||
|
PKG_VERSION:=2
|
||||||
|
|
||||||
|
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||||
|
|
||||||
|
include ../gluon.mk
|
||||||
|
|
||||||
|
define Package/gluon-hoodselector
|
||||||
|
SECTION:=gluon
|
||||||
|
CATEGORY:=Gluon
|
||||||
|
TITLE:=Automatically sorte routers into network hoods.
|
||||||
|
DEPENDS:=+gluon-site +gluon-mesh-batman-adv-15 @GLUON_MULTIDOMAIN
|
||||||
|
CONFLICTS:=+gluon-config-mode-domain-select
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/gluon-hoodselector/description
|
||||||
|
This is the hoodselector. The hoodselector is one of the main components for
|
||||||
|
splitting a layer 2 mesh network into seperated network segments (hoods).
|
||||||
|
The job of the hoodselector is to automatically detect in which hood
|
||||||
|
the router is located based on geo settings or by scanning its environment.
|
||||||
|
Based on these informations the hoodselector should select a hood from a
|
||||||
|
list of known hoods (hoodlist) and adjust vpn, wireless and mesh on lan
|
||||||
|
configuration based on the settings given for the selected hood.
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackageGluon,gluon-hoodselector))
|
37
package/gluon-hoodselector/check_site.lua
Normal file
37
package/gluon-hoodselector/check_site.lua
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
need_string_match(in_domain({'domain_seed'}), '^' .. ('%x'):rep(64) .. '$')
|
||||||
|
|
||||||
|
function need_nil(path)
|
||||||
|
need(path, function(val)
|
||||||
|
if val == nil then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end, true, "default hood should not contain shapes")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--Need to check if not default hood and does the default not contain shapes
|
||||||
|
if this_domain() ~= need_string(in_site({'default_domain'})) then
|
||||||
|
local no_shapes = true
|
||||||
|
for _,shape in ipairs(need_table(in_domain({'hoodselector', 'shapes'}))) do
|
||||||
|
no_shapes = false
|
||||||
|
if #shape >= 2 then
|
||||||
|
for _, pos in ipairs(shape) do
|
||||||
|
if pos.lat == nil or not ( pos.lat < 90.0 and pos.lat > -90.0 ) then
|
||||||
|
need(in_domain({'hoodselector', 'shapes'}), function(err) return false end, true, "lat muss match a rage +/-90.0")
|
||||||
|
end
|
||||||
|
if pos.lon == nil or not ( pos.lon < 180.0 and pos.lon > -180.0 ) then
|
||||||
|
need(in_domain({'hoodselector', 'shapes'}), function(err) return false end, true, "lon muss match a rage +/-180.0")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #shape < 2 then
|
||||||
|
need(in_domain({'hoodselector', 'shapes'}), function(err) return false end, true, "needs to have at lease 2 coordiantes for rectangular shapes.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if no_shapes then
|
||||||
|
need(in_domain({'hoodselector', 'shapes'}), function(err) return false end, true, "no shapes are defined in hoods")
|
||||||
|
end
|
||||||
|
else -- ente by default hood
|
||||||
|
need_nil(in_domain({'hoodselector', 'shapes'}))
|
||||||
|
end
|
@ -0,0 +1 @@
|
|||||||
|
*/2 * * * * /usr/sbin/hoodselector
|
@ -0,0 +1,191 @@
|
|||||||
|
local fs = require 'nixio.fs'
|
||||||
|
local json = require 'jsonc'
|
||||||
|
local uci = require('simple-uci').cursor()
|
||||||
|
local site = require 'gluon.site'
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.split(s, delimiter)
|
||||||
|
local result = {};
|
||||||
|
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
|
||||||
|
table.insert(result, match);
|
||||||
|
end
|
||||||
|
return result;
|
||||||
|
end
|
||||||
|
|
||||||
|
local PID = M.split(io.open("/proc/self/stat", 'r'):read('*a'), " ")[1]
|
||||||
|
|
||||||
|
function M.log(msg)
|
||||||
|
if msg then
|
||||||
|
io.stdout:write(msg.."\n")
|
||||||
|
os.execute("logger hoodselector["..PID.."]: "..msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- bool if direct VPN. The detection is realaise by searching the fastd network interface inside the originator table
|
||||||
|
function M.directVPN(vpnIfaceList)
|
||||||
|
for _,vpnIface in ipairs(vpnIfaceList) do
|
||||||
|
local file = io.open("/sys/kernel/debug/batman_adv/bat0/originators", 'r')
|
||||||
|
if file ~= nil then
|
||||||
|
for outgoingIF in file:lines() do
|
||||||
|
-- escape special chars "[]-"
|
||||||
|
if outgoingIF:match(string.gsub("%[ " .. vpnIface .. "%]","%-", "%%-")) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.fastd_installed()
|
||||||
|
if io.open("/usr/bin/fastd", 'r') ~= nil then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.tunneldigger_installed()
|
||||||
|
if io.open("/usr/bin/tunneldigger", 'r') ~= nil then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get Geoposition. Return nil for no position
|
||||||
|
function M.getGeolocation()
|
||||||
|
return {["lat"] = tonumber(uci:get('gluon-node-info', uci:get_first('gluon-node-info', 'location'), 'latitude')),
|
||||||
|
["lon"] = tonumber(uci:get('gluon-node-info', uci:get_first('gluon-node-info', 'location'), 'longitude')) }
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.get_domains()
|
||||||
|
local list = {}
|
||||||
|
for domain_path in fs.glob('/lib/gluon/domains/*.json') do
|
||||||
|
table.insert(list, {
|
||||||
|
domain_code = domain_path:match('([^/]+)%.json$'),
|
||||||
|
domain = assert(json.load(domain_path)),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
return list
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Source with pseudocode: https://de.wikipedia.org/wiki/Punkt-in-Polygon-Test_nach_Jordan
|
||||||
|
-- see also https://en.wikipedia.org/wiki/Point_in_polygon
|
||||||
|
-- parameters: points A = (x_A,y_A), B = (x_B,y_B), C = (x_C,y_C)
|
||||||
|
-- return value: −1 if the ray from A to the right bisects the edge [BC] (the lower vortex of [BC]
|
||||||
|
-- is not seen as part of [BC]);
|
||||||
|
-- 0 if A is on [BC];
|
||||||
|
-- +1 else
|
||||||
|
function M.crossProdTest(x_A,y_A,x_B,y_B,x_C,y_C)
|
||||||
|
if y_A == y_B and y_B == y_C then
|
||||||
|
if (x_B <= x_A and x_A <= x_C) or (x_C <= x_A and x_A <= x_B) then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
if not ((y_A == y_B) and (x_A == x_B)) then
|
||||||
|
if y_B > y_C then
|
||||||
|
-- swap B and C
|
||||||
|
local h = x_B
|
||||||
|
x_B = x_C
|
||||||
|
x_C = h
|
||||||
|
h = y_B
|
||||||
|
y_B = y_C
|
||||||
|
y_C = h
|
||||||
|
end
|
||||||
|
if (y_A <= y_B) or (y_A > y_C) then
|
||||||
|
return 1
|
||||||
|
end
|
||||||
|
local Delta = (x_B-x_A) * (y_C-y_A) - (y_B-y_A) * (x_C-x_A)
|
||||||
|
if Delta > 0 then
|
||||||
|
return 1
|
||||||
|
elseif Delta < 0 then
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Source with pseudocode: https://de.wikipedia.org/wiki/Punkt-in-Polygon-Test_nach_Jordan
|
||||||
|
-- see also: https://en.wikipedia.org/wiki/Point_in_polygon
|
||||||
|
-- let P be a 2D Polygon and Q a 2D Point
|
||||||
|
-- return value: +1 if Q within P;
|
||||||
|
-- −1 if Q outside of P;
|
||||||
|
-- 0 if Q on an edge of P
|
||||||
|
function M.pointInPolygon(poly, point)
|
||||||
|
local t = -1
|
||||||
|
for i=1,#poly-1 do
|
||||||
|
t = t * M.crossProdTest(point.lon,point.lat,poly[i][2],poly[i][1],poly[i+1][2],poly[i+1][1])
|
||||||
|
if t == 0 then break end
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return hood from the hood file based on geo position or nil if no real hood could be determined
|
||||||
|
-- First check if an area has > 2 points and is hence a polygon. Else assume it is a rectangular
|
||||||
|
-- box defined by two points (south-west and north-east)
|
||||||
|
function M.getHoodByGeo(jhood,geo)
|
||||||
|
for _, hood in pairs(jhood) do
|
||||||
|
if hood.domain_code ~= site.default_domain() then
|
||||||
|
for _, area in pairs(hood.domain.hoodselector.shapes) do
|
||||||
|
if #area > 2 then
|
||||||
|
if (M.pointInPolygon(area,geo) == 1) then
|
||||||
|
return hood
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if ( geo.lat >= area[1][1] and geo.lat < area[2][1] and geo.lon >= area[1][2]
|
||||||
|
and geo.lon < area[2][2] ) then
|
||||||
|
return hood
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.set_hoodconfig(geoHood)
|
||||||
|
if uci:get('gluon', 'core', 'domain') ~= geoHood.domain_code then
|
||||||
|
uci:set('gluon', 'core', 'domain', geoHood.domain_code)
|
||||||
|
uci:save('gluon')
|
||||||
|
uci:commit('gluon') -- necessary?
|
||||||
|
os.execute('gluon-reconfigure')
|
||||||
|
io.stdout:write("Set hood \""..geoHood.domain.domain_names[geoHood.domain_code].."\"\n")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return the default hood in the hood list.
|
||||||
|
-- This method can return the following data:
|
||||||
|
-- * default hood
|
||||||
|
-- * nil if no default hood has been defined
|
||||||
|
function M.getDefaultHood(jhood)
|
||||||
|
for _, h in pairs(jhood) do
|
||||||
|
if h.domain_code == site.default_domain() then
|
||||||
|
return h
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.restart_services()
|
||||||
|
local procTBL = {
|
||||||
|
"fastd",
|
||||||
|
"tunneldigger",
|
||||||
|
"network",
|
||||||
|
}
|
||||||
|
|
||||||
|
for proc in ipairs(procTBL) do
|
||||||
|
if io.open("/etc/init.d/"..proc, 'r') ~= nil then
|
||||||
|
print(proc.." restarting ...")
|
||||||
|
os.execute("/etc/init.d/"..proc.." restart")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if io.open("/etc/config/wireless", 'r') then
|
||||||
|
print("wifi restarting ...")
|
||||||
|
os.execute("wifi")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
182
package/gluon-hoodselector/luasrc/usr/sbin/hoodselector
Executable file
182
package/gluon-hoodselector/luasrc/usr/sbin/hoodselector
Executable file
@ -0,0 +1,182 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
|
||||||
|
-- PID file to ensure the hoodselector isn't running parallel
|
||||||
|
local pidPath="/var/run/hoodselector.pid"
|
||||||
|
|
||||||
|
local uci = require('simple-uci').cursor()
|
||||||
|
local hoodutil = require("hoodselector.util")
|
||||||
|
|
||||||
|
if io.open(pidPath, "r") ~=nil then
|
||||||
|
hoodutil.log("The hoodselector is still running.")
|
||||||
|
os.exit(1)
|
||||||
|
else
|
||||||
|
if io.open(pidPath, "w") ==nil then
|
||||||
|
hoodutil.log("Can`t create pid file on "..pidPath)
|
||||||
|
os.exit(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Program terminating function including removing of PID file
|
||||||
|
local function exit(exc)
|
||||||
|
if io.open(pidPath, "r") ~=nil then
|
||||||
|
os.remove(pidPath)
|
||||||
|
end
|
||||||
|
os.exit(tonumber(exc))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- initialization done
|
||||||
|
|
||||||
|
local function get_mesh_vpn_interface()
|
||||||
|
local ret = {}
|
||||||
|
if hoodutil.fastd_installed() then
|
||||||
|
local vpnifac = uci:get('fastd', 'mesh_vpn_backbone', 'net')
|
||||||
|
if vpnifac ~= nil then
|
||||||
|
vpnifac = vpnifac:gsub("%_",'-')
|
||||||
|
table.insert(ret,vpnifac)
|
||||||
|
else
|
||||||
|
hoodutil.log("fastd uci config broken! abort...")
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if hoodutil.tunneldigger_installed() then
|
||||||
|
local vpnifac = uci:get('tunneldigger', 'mesh_vpn', 'interface')
|
||||||
|
if vpnifac ~= nil then
|
||||||
|
table.insert(ret,vpnifac)
|
||||||
|
else
|
||||||
|
hoodutil.log("tunneldigger uci config broken! abort...")
|
||||||
|
exit(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
-- INITIALIZE AND PREPARE DATA --
|
||||||
|
-- read hoodfile...
|
||||||
|
local jhood = hoodutil.get_domains()
|
||||||
|
|
||||||
|
-- get defaul hood
|
||||||
|
local defaultHood = hoodutil.getDefaultHood(jhood)
|
||||||
|
|
||||||
|
|
||||||
|
-- VPN MODE
|
||||||
|
-- If we have a VPN connection then we will try to get the routers location and
|
||||||
|
-- select the hood coresponding to our location.
|
||||||
|
-- If no hood for the location has been defined, we will select
|
||||||
|
-- the default hood.
|
||||||
|
-- If we can not get our routers location, we will fallback to scan mode.
|
||||||
|
if hoodutil.directVPN(get_mesh_vpn_interface()) then
|
||||||
|
io.stdout:write('VPN connection found.\n')
|
||||||
|
local geo = hoodutil.getGeolocation()
|
||||||
|
if geo.lat ~= nil and geo.lon ~= nil then
|
||||||
|
io.stdout:write('Position found.\n')
|
||||||
|
local geoHood = hoodutil.getHoodByGeo(jhood, geo)
|
||||||
|
if geoHood ~= nil then
|
||||||
|
if hoodutil.set_hoodconfig(geoHood) then
|
||||||
|
hoodutil.restart_services() -- TMP solution
|
||||||
|
io.stdout:write('Hood set by VPN mode.\n')
|
||||||
|
end
|
||||||
|
exit(0)
|
||||||
|
end
|
||||||
|
io.stdout:write('No hood has been defined for current position.\n')
|
||||||
|
if hoodutil.set_hoodconfig(defaultHood) then
|
||||||
|
hoodutil.restart_services() -- TMP solution
|
||||||
|
io.stdout:write('Hood set by VPN mode.\n')
|
||||||
|
end
|
||||||
|
exit(0)
|
||||||
|
else
|
||||||
|
io.stdout:write('No position found\n')
|
||||||
|
end
|
||||||
|
else
|
||||||
|
io.stdout:write('No VPN connection found\n')
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
|
||||||
|
if hoodutil.batmanHasGateway() then
|
||||||
|
441 io.stdout:write('Batman gateways found\n')
|
||||||
|
442 local gw_intf = {}
|
||||||
|
443 table.insert(gw_intf,hoodutil.get_batman_GW_interface())
|
||||||
|
444
|
||||||
|
445 if next(gw_intf) then
|
||||||
|
446 -- wifi interface
|
||||||
|
447 local bssidHood = hoodutil.get_radio_to_bssid(radios,gw_intf[1], jhood)
|
||||||
|
448 if bssidHood ~= nil then
|
||||||
|
449 --check if hood inside geo pos
|
||||||
|
450 local geo = hoodutil.getGeolocation()
|
||||||
|
451 if geo.lat ~= nil and geo.lon ~= nil then
|
||||||
|
452 io.stdout:write('Position found.\n')
|
||||||
|
453 local geoHood = hoodutil.getHoodByGeo(jhood, geo)
|
||||||
|
454 if geoHood ~= nil then
|
||||||
|
455 if string.format(hash.md5(table.tostring(bssidHood))) ~=
|
||||||
|
string.format(hash.md5(table.tostring(geoHood))) then
|
||||||
|
456 io.stdout:write('Geo hood and bssid hood are not equal, do wifi scan...\n')
|
||||||
|
457 local sortedWlanList = hoodutil.wlan_list_sorted(radios, mesh_prefix)
|
||||||
|
458 for i=#sortedWlanList,1,-1 do
|
||||||
|
459 if(string.lower(geoHood.bssid) ~= string.lower(sortedWlanList[i].bssid)) then
|
||||||
|
460 table.remove(sortedWlanList, i)
|
||||||
|
461 end
|
||||||
|
462 end
|
||||||
|
463 if next(sortedWlanList) then
|
||||||
|
464 io.stdout:write('Try to switch back in our real hood!\n')
|
||||||
|
465 io.stdout:write('After filtering we will test the following wireless networks:\n')
|
||||||
|
466 for _, network in pairs(sortedWlanList) do
|
||||||
|
467 print(network["quality"].."\t"..network["frequency"].."\t"..network["bssid"].."\t"..network["ssid"])
|
||||||
|
468 end
|
||||||
|
469 io.stdout:write("Prepare configuration for testing wireless networks...\n")
|
||||||
|
470 local bssid = hoodutil.test_batman_mesh_networks(sortedWlanList, mesh_prefix)
|
||||||
|
471 hoodutil.wireless_restart()
|
||||||
|
472 io.stdout:write("Finished testing wireless networks, restored previous configuration\n")
|
||||||
|
473 if bssid ~= nil then
|
||||||
|
474 set_hoodconfig(geoHood, mesh_prefix, radios)
|
||||||
|
475 hoodutil.vpn_enable()
|
||||||
|
476 hoodutil.vpn_start()
|
||||||
|
477 io.stdout:write('Set Geo Hood by Gateway mode\n')
|
||||||
|
478 write_molwm(geoHood, radios)
|
||||||
|
479 exit(0)
|
||||||
|
480 else
|
||||||
|
481 io.stdout:write('No neighboring freifunk batman advanced mesh found.\n')
|
||||||
|
482 end
|
||||||
|
483 else
|
||||||
|
484 io.stdout:write('No networks left after filtering!\n')
|
||||||
|
485 end --end next(sortedWlanList)
|
||||||
|
486 else
|
||||||
|
487 io.stdout:write('Geo hood are equal to bssid hood no wifi scan necessary.\n')
|
||||||
|
488 end --end bssidHood ~= geoHood
|
||||||
|
489 else
|
||||||
|
490 io.stdout:write('No hood has been defined for current position.\n')
|
||||||
|
491 end --end geoHood ~= nil
|
||||||
|
492 else
|
||||||
|
493 io.stdout:write('No position found.\n')
|
||||||
|
494 end --end geo.lat ~= nil and geo.lon ~= nil
|
||||||
|
495 set_hoodconfig(bssidHood, mesh_prefix, radios)
|
||||||
|
496 io.stdout:write('Hood set by batmanHasGateway mode, GW source is wifi\n')
|
||||||
|
497 write_molwm(bssidHood,radios)
|
||||||
|
498 exit(0)
|
||||||
|
499 end --end bssidHood ~= nil
|
||||||
|
500
|
||||||
|
501 -- mesh lan or wan interface
|
||||||
|
502 if hoodutil.mesh_lan_wan(gw_intf[1]) then
|
||||||
|
503 -- if mesh_lan/wan try to get hood by selected bssid of neightbour vpnRouters
|
||||||
|
504 local neighbourBssid = hoodutil.molw_get_bssid(gw_intf)
|
||||||
|
505 if neighbourBssid ~= nil then
|
||||||
|
506 bssidHood = hoodutil.gethoodByBssid(jhood, neighbourBssid)
|
||||||
|
507 if bssidHood ~= nil then
|
||||||
|
508 set_hoodconfig(bssidHood, mesh_prefix, radios)
|
||||||
|
509 io.stdout:write('Hood set by batmanHasGateway mode, GW source is mesh on lan/wan\n')
|
||||||
|
510 molwmtable["md5hash"] = "\"" .. string.format(hash.md5(table.tostring(bssidHood))) .. "\""
|
||||||
|
511 molwmtable["hoodname"] = "\"" .. bssidHood["name"] .. "\""
|
||||||
|
512 molwmtable["bssid"] = "\"" .. bssidHood["bssid"] .. "\""
|
||||||
|
513 molwm_to_file()
|
||||||
|
514 exit(0)
|
||||||
|
515 end
|
||||||
|
516 end
|
||||||
|
517 end
|
||||||
|
518 end --end next(gw_intf)
|
||||||
|
519 local currendHood = hoodutil.getCurrentHood(jhood)
|
||||||
|
520 if currendHood ~= nil then
|
||||||
|
521 write_molwm(currendHood,radios)
|
||||||
|
522 end
|
||||||
|
523 end
|
||||||
|
--]]
|
||||||
|
|
||||||
|
exit(0) -- Debug
|
Loading…
Reference in New Issue
Block a user