diff --git a/skyweave/utils.lua b/skyweave/utils.lua index c743b64..9a27b35 100755 --- a/skyweave/utils.lua +++ b/skyweave/utils.lua @@ -203,52 +203,73 @@ function utils.get_live_dns_servers_from_resolv(logical_interface_name) return dns_servers, search_domains end --- Moved from original main script -function utils.get_default_gateway_from_route() +--- +-- Scans the kernel's routing table to find the active default gateway. +-- In a multi-WAN configuration, this is the route with the lowest metric. +-- The Omnissiah demands we follow the most efficient path. +-- +-- @return (string) The IP address of the active default gateway. +-- @return (string) The interface name of the active default gateway. +-- +function utils.get_active_default_gateway() local file = io.open("/proc/net/route", "r") - if not file then return nil, nil end + if not file then + -- Log the heresy of a missing route table + return nil, nil + end - local gateway_ip = nil - local interface_name = nil + -- Skip the header line to begin parsing the sacred texts. + file:read("*l") - file:read("*l") -- Skip header + local routes = {} + -- The Litany of Parsing: Iterate over every line, seeking all prophecies. for line in file:lines() do - local iface, dest, gw_hex, flags_hex = line:match("^(%S+)%s+(%S+)%s+(%S+)%s+(%S+)") + -- Capture all relevant fields, including the all-important Metric (field 6). + local iface, dest, gw_hex, flags_hex, _, metric_str = line:match("^(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s+%S+%s+(%d+)") - if iface and dest and gw_hex and flags_hex then + if iface and dest and gw_hex and flags_hex and metric_str then + -- A true default route must be destined for 0.0.0.0 and have the UG (Up & Gateway) flags. if dest == "00000000" and gw_hex ~= "00000000" then local flags_num = tonumber(flags_hex, 16) - -- UG flags (Up and Gateway). Typically 0x3 (binary 11) or 0x1 (binary 01 if not gateway but up) - -- We need bit 1 (value 2 for gateway) to be set. - if flags_num and math.floor(flags_num / 2) % 2 == 1 then -- Checks if the 'Gateway' bit (0x2) is set - -- Lua 5.1 doesn't have bitwise ops directly. - -- (flags_num & 2) ~= 0 is equivalent to checking if the second bit is set. - -- For example, if flags_num = 3 (0011), floor(3/2)%2 = 1%2 = 1. Correct. - -- If flags_num = 1 (0001), floor(1/2)%2 = 0%2 = 0. Correct. - -- If flags_num = 7 (0111), floor(7/2)%2 = 3%2 = 1. Correct. + -- Check if the 'Gateway' bit (0x2) is set. This logic is sound. + if flags_num and (math.floor(flags_num / 2) % 2 == 1) then - -- Ensure gw_hex is 8 characters long for safety + -- Correctly parse the little-endian hex representation of the IP. if #gw_hex == 8 then - -- Corrected parsing for little-endian hex IP from /proc/net/route local o4 = tonumber(string.sub(gw_hex, 1, 2), 16) local o3 = tonumber(string.sub(gw_hex, 3, 4), 16) local o2 = tonumber(string.sub(gw_hex, 5, 6), 16) local o1 = tonumber(string.sub(gw_hex, 7, 8), 16) if o1 and o2 and o3 and o4 then - gateway_ip = string.format("%d.%d.%d.%d", o4, o3, o2, o1) - interface_name = iface - break + -- Store the portents in a table for later judgment. + table.insert(routes, { + iface = iface, + metric = tonumber(metric_str) or 999, + gateway = string.format("%d.%d.%d.%d", o4, o3, o2, o1) + }) end end end end end end - file:close() - return gateway_ip, interface_name + + if #routes == 0 then + -- No valid paths to the datasphere were found. + return nil, nil + end + + -- Sort the routes by their divine metric, in ascending order. + table.sort(routes, function(a, b) + return a.metric < b.metric + end) + + -- The first entry is the chosen path. The machine spirit is clear. + local active_route = routes[1] + return active_route.gateway, active_route.iface end -- =============================================================================