Avoid cutting off leaf nodes (#1211)

* Avoid cutting off leaf nodes

* Use OLSR broadcast with pending nodes
This commit is contained in:
Tim Wilkinson 2024-05-21 21:02:58 -07:00 committed by GitHub
parent 2509ebdf3b
commit fbaa54bb23
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 158 additions and 96 deletions

View File

@ -52,6 +52,7 @@ local speed_time = 10 --
local speed_limit = 1000 -- close connection if it's too slow (< 1kB/s for 10 seconds)
local default_short_retries = 20 -- More link-level retries helps overall tcp performance (factory default is 7)
local default_long_retries = 20 -- (factory default is 4)
local default_min_routes = 8 -- Minimum number of routes (nodes x2 usually) on a link which *must* be left active. Avoids cutting off poorly behaving leaf nodes.
local NFT = "/usr/sbin/nft"
local IW = "/usr/sbin/iw"
@ -62,6 +63,9 @@ local IPCMD = "/sbin/ip"
local now = 0
local config = {}
local total_node_route_count = 0
local minroutes_count = 0
function update_config()
local c = uci.cursor() -- each time as /etc/config/aredn may have changed
config = {
@ -75,6 +79,7 @@ function update_config()
min_quality = tonumber(c:get("aredn", "@lqm[0]", "min_quality")),
margin_quality = tonumber(c:get("aredn", "@lqm[0]", "margin_quality")),
ping_penalty = tonumber(c:get("aredn", "@lqm[0]", "ping_penalty")),
min_routes = tonumber(c:get("aredn", "@lqm[0]", "min_routes") or default_min_routes),
user_blocks = c:get("aredn", "@lqm[0]", "user_blocks") or "",
user_allows = c:get("aredn", "@lqm[0]", "user_allows") or ""
}
@ -112,6 +117,8 @@ function should_block(track)
return track.blocks.user
elseif is_pending(track) then
return track.blocks.dtd or track.blocks.user
elseif track.minroutes then
return false
else
return track.blocks.dtd or track.blocks.signal or track.blocks.distance or track.blocks.user or track.blocks.dup or track.blocks.quality
end
@ -229,6 +236,13 @@ function round(v)
return math.floor(v + 0.5)
end
function set_minroutes(track)
if track.node_route_count <= config.min_routes or (total_node_route_count - track.node_route_count) <= config.min_routes then
track.minroutes = true
minroutes_count = minroutes_count + 1
end
end
-- Canonical hostname
function canonical_hostname(hostname)
return hostname and hostname:lower():gsub("^dtdlink%.",""):gsub("^mid%d+%.",""):gsub("^xlink%d+%.",""):gsub("%.local%.mesh$", "")
@ -304,6 +318,7 @@ function lqm()
local last_coverage = -1
local last_short_retries = -1
local last_long_retries = -1
local pending_count = 0
while true
do
now = nixio.sysinfo().uptime
@ -523,7 +538,9 @@ function lqm()
last_tx_retries = nil,
avg_tx = nil,
avg_tx_retries = nil,
avg_tx_fail = nil
avg_tx_fail = nil,
node_route_count = 0,
minroutes = false
}
end
local track = tracker[station.mac]
@ -568,10 +585,17 @@ function lqm()
end
-- Update link tracking state
local ip2tracker = {}
pending_count = 0
for _, track in pairs(tracker)
do
if not track.ip then
track.routable = false
else
ip2tracker[track.ip] = track
-- Update if link is routable
local rt = track.ip and ip.route(track.ip) or nil
local rt = ip.route(track.ip)
if rt and tostring(rt.gw) == track.ip then
track.routable = true
else
@ -579,7 +603,7 @@ function lqm()
end
-- Refresh remote attributes periodically as this is expensive
if track.ip and now > track.refresh then
if now > track.refresh then
-- Refresh the hostname periodically as it can change
track.hostname = canonical_hostname(nixio.getnameinfo(track.ip)) or track.hostname
@ -669,6 +693,7 @@ function lqm()
end
end
end
end
-- Update avg snr using both ends (if we have them)
if track.snr then
@ -681,6 +706,11 @@ function lqm()
track.avg_snr = null
end
-- Count number of pending trackers
if is_pending(track) then
pending_count = pending_count + 1
end
-- Ping addresses and penalize quality for excessively slow links
if should_ping(track) then
local success = 100
@ -748,13 +778,32 @@ function lqm()
if track.quality and track.quality == 0 and not track.quality0_seen then
track.quality0_seen = now
end
track.node_route_count = 0
end
--
-- Pull in the routing table to see how many node routes are associated with each tracker.
--
total_node_route_count = 0
for _, route in ipairs(aredn.olsr.getOLSRRoutes())
do
-- Count routes to nodes. There are two routes to most nodes, the node's primary address
-- and the node's dtdlink address.
if route.genmask == 32 and route.destination:match("^10%.") then
local track = ip2tracker[route.gateway];
if track then
track.node_route_count = track.node_route_count + 1
total_node_route_count = total_node_route_count + 1
end
end
end
--
-- At this point we have gather all the data we need to determine which links are best to use and
-- which links should be blocked.
--
minroutes_count = 0
for _, track in pairs(tracker)
do
for _ = 1,1
@ -768,6 +817,7 @@ function lqm()
pair = false,
quality = false
}
track.minroutes = false
-- Always allow if user requested it
for val in string.gmatch(config.user_allows, "([^,]+)")
@ -799,6 +849,7 @@ function lqm()
-- Block any nodes which are too distant
if track.distance and (track.distance < config.min_distance or track.distance > config.max_distance) then
track.blocks.distance = true
set_minroutes(track)
break
end
@ -819,12 +870,14 @@ function lqm()
if not oldblocks.signal then
if track.snr < config.low or (track.rev_snr and track.rev_snr < config.low) then
track.blocks.signal = true
set_minroutes(track)
break
end
-- when blocked link becomes (low+margin) again, unblock
-- when blocked link becomes (low+margin) again, dont maintain block
else
if track.snr < config.low + config.margin or (track.rev_snr and track.rev_snr < config.low + config.margin) then
track.blocks.signal = true
set_minroutes(track)
break
else
-- When signal is good enough to unblock a link but the quality is low, artificially bump
@ -842,10 +895,12 @@ function lqm()
if not oldblocks.quality then
if track.quality < config.min_quality then
track.blocks.quality = true
set_minroutes(track)
end
else
if track.quality < config.min_quality + config.margin_quality then
track.blocks.quality = true
set_minroutes(track)
end
end
end
@ -906,7 +961,6 @@ function lqm()
-- Reflect this state with the firewall and various other
-- node parameters (e.g. rts, coverage)
--
local distance = -1
-- Update the block state and calculate the routable distance
for _, track in pairs(tracker)
@ -1000,13 +1054,17 @@ function lqm()
trackers = tracker,
distance = distance,
coverage = coverage,
hidden_nodes = hidden_nodes
hidden_nodes = hidden_nodes,
total_node_route_count = total_node_route_count
}, true))
f:close()
end
-- Save valid (unblocked) rf mac list for use by OLSR
if config.enable and phy ~= "none" then
if minroutes_count > 0 or pending_count > 0 then
os.remove( "/tmp/lqm." .. phy .. ".macs")
else
local tmpfile = "/tmp/lqm." .. phy .. ".macs.tmp"
f = io.open(tmpfile, "w")
if f then
@ -1021,6 +1079,7 @@ function lqm()
os.remove(tmpfile)
end
end
end
wait_for_ticks(60) -- 1 minute
end

View File

@ -761,6 +761,9 @@ do
else
lqmstatus = "idle"
end
if track.minroutes then
lqmstatus = "minroutes"
end
if track.snr then
c4b = track.snr
if track.rev_snr then