From 8dba04a1d0e5f267cc897f210b2b48248e019f25 Mon Sep 17 00:00:00 2001 From: kitadmin Date: Thu, 23 Jul 2026 02:19:18 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20auto-detect=20WAN=20roles=20=E2=80=94=20?= =?UTF-8?q?cellular=20vs=20fiber?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Detect Eyeride cellular interface by checking 192.168.10.x subnet - Map correctly: ethernet/fiber → wan, Eyeride 5G → modem_0001 - Add carrier detection: AT&T for fiber gateways, Eyeride for cellular - Fix sel_primary: fiber is active by default unless cellular route exists - Fixes console showing 'on cellular' when actually on AT&T fiber Co-Authored-By: Claude Opus 4.8 --- .../usr/lib/busrouter/telemetry-synology.sh | 137 +++++++++++++++--- 1 file changed, 117 insertions(+), 20 deletions(-) diff --git a/package/kit-busrouter/files/usr/lib/busrouter/telemetry-synology.sh b/package/kit-busrouter/files/usr/lib/busrouter/telemetry-synology.sh index 93abafd..66553cb 100755 --- a/package/kit-busrouter/files/usr/lib/busrouter/telemetry-synology.sh +++ b/package/kit-busrouter/files/usr/lib/busrouter/telemetry-synology.sh @@ -293,6 +293,76 @@ _collect_wan() { # ── Telemetry Collection ─────────────────────────────────────────────────── +# Detect which interface is the Eyeride cellular link. +# The Eyeride runs on 192.168.10.x subnet; the interface with an IP in that +# range is the cellular path. The other WAN interface is the primary/fiber uplink. +# Returns: cellular_iface ethernet_iface +_detect_wan_roles() { + # Check if WAN2 is on the Eyeride subnet + _wan2_ip=$(ip addr show "$WAN2_IFACE" 2>/dev/null | grep 'inet ' | awk '{print $2}' | cut -d/ -f1) + if echo "$_wan2_ip" | grep -q '^192\.168\.10\.'; then + echo "$WAN2_IFACE $WAN1_IFACE" # WAN2 = cellular, WAN1 = ethernet + else + # Check if WAN1 is on the Eyeride subnet + _wan1_ip=$(ip addr show "$WAN1_IFACE" 2>/dev/null | grep 'inet ' | awk '{print $2}' | cut -d/ -f1) + if echo "$_wan1_ip" | grep -q '^192\.168\.10\.'; then + echo "$WAN1_IFACE $WAN2_IFACE" # WAN1 = cellular, WAN2 = ethernet + else + # Default: WAN2 is cellular (Eyeride typically on secondary port) + echo "$WAN2_IFACE $WAN1_IFACE" + fi + fi +} + +# Derive carrier name for an interface. Uses reverse DNS on gateway or ISP detection. +# For the Eyeride (192.168.10.x), the carrier comes from the Eyeride's cellular operator. +_detect_carrier() { + _iface="$1" + _gw=$(ip route show default 2>/dev/null | grep "$_iface" | awk '{print $3}' | head -1) + # Check if this is the Eyeride network + _ip=$(ip addr show "$_iface" 2>/dev/null | grep 'inet ' | awk '{print $2}' | cut -d/ -f1) + if echo "$_ip" | grep -q '^192\.168\.10\.'; then + # This is the Eyeride — carrier comes from the Eyeride's cellular operator + _sig=$(_eyeride_signal 2>/dev/null) + _sig_carrier=$(echo "$_sig" | awk '{print $5}') + if [ -n "$_sig_carrier" ] && [ "$_sig_carrier" != "null" ] && [ "$_sig_carrier" != "Eyeride" ]; then + echo "$_sig_carrier"; return + fi + echo "Eyeride"; return + fi + # For the primary/ethernet interface, try to detect from gateway + if [ -n "$_gw" ]; then + # Common AT&T gateways + if echo "$_gw" | grep -qE '^192\.168\.[01]\.'; then + echo "AT&T"; return + fi + # Common Spectrum/Charter + if echo "$_gw" | grep -qE '^192\.168\.[0-9]+\.'; then + _rev=$(nslookup "$_gw" 2>/dev/null | grep -i name | head -1 | sed 's/.*= //') + if echo "$_rev" | grep -qi 'att\|sbcglobal\|bellsouth'; then + echo "AT&T"; return + fi + fi + fi + # Fallback + echo "" +} + +# Add carrier and identity fields to a WAN JSON object. +# Usage: _add_wan_identity +_add_wan_identity() { + _json="$1" _iface="$2" + _carrier=$(_detect_carrier "$_iface") + _esc_carrier=$(_json_str "${_carrier}") + # Strip closing brace, add carrier, re-close + printf '%s' "$_json" | sed 's/}$//' + if [ -n "$_carrier" ]; then + printf ',"carrier":"%s"}' "${_esc_carrier}" + else + printf '}' + fi +} + # Build the full telemetry JSON payload (canonical GL format). # Emits JSON to stdout; never fails — uses defaults for missing fields. telemetry_collect() { @@ -309,42 +379,69 @@ telemetry_collect() { gps_fix=$(echo "$gps_out" | awk '{print $3}') fi - # WAN1 (primary uplink, typically Ethernet → modem_0001 in canonical format) - wan1_json=$(_collect_wan "$WAN1_IFACE") + # Auto-detect which interface is cellular vs ethernet/fiber + _cell_iface="" _eth_iface="" + if _roles=$(_detect_wan_roles 2>/dev/null); then + _cell_iface=$(echo "$_roles" | awk '{print $1}') + _eth_iface=$(echo "$_roles" | awk '{print $2}') + else + _cell_iface="$WAN2_IFACE" + _eth_iface="$WAN1_IFACE" + fi - # WAN2 (Eyeride cellular → wan in canonical format) - wan2_json=$(_collect_wan "$WAN2_IFACE") + # Collect metrics for both WANs + _eth_json=$(_collect_wan "$_eth_iface") + _cell_json=$(_collect_wan "$_cell_iface") - # Eyeride cellular signal for WAN2 (try to enrich with rsrp/rsrq/sinr) + # Add carrier identity to each WAN + _eth_json=$(_add_wan_identity "$_eth_json" "$_eth_iface") + _cell_json=$(_add_wan_identity "$_cell_json" "$_cell_iface") + + # Eyeride cellular signal for the cellular interface eyeride_sig=$(_eyeride_signal 2>/dev/null) _rsrp=$(echo "$eyeride_sig" | awk '{print $1}') _rsrq=$(echo "$eyeride_sig" | awk '{print $2}') _sinr=$(echo "$eyeride_sig" | awk '{print $3}') _tech=$(echo "$eyeride_sig" | awk '{print $4}') - _carrier=$(echo "$eyeride_sig" | awk '{print $5}') + # Add cellular signal fields to the cellular WAN JSON + if echo "$_cell_json" | grep -q '"loss_pct"'; then + _cell_json=$(printf '%s' "$_cell_json" | sed 's/}$//') + if [ "$_rsrp" != "null" ] && [ -n "$_rsrp" ]; then + _esc_tech=$(_json_str "${_tech:-unknown}") + _cell_json="${_cell_json},\"rsrp_dbm\":${_rsrp},\"rsrq_db\":${_rsrq},\"sinr_db\":${_sinr},\"technology\":\"${_esc_tech}\"}" + else + _cell_json="${_cell_json}}" + fi + fi - # Build WAN2 JSON with cellular signal if available - if [ "$_rsrp" != "null" ] && [ -n "$_rsrp" ]; then - # Replace closing brace with signal fields - wan2_json=$(printf '%s' "$wan2_json" | sed 's/}$//') - _esc_tech=$(_json_str "${_tech:-unknown}") - _esc_carrier=$(_json_str "${_carrier:-Eyeride}") - wan2_json="${wan2_json},\"rsrp_dbm\":${_rsrp},\"rsrq_db\":${_rsrq},\"sinr_db\":${_sinr},\"technology\":\"${_esc_tech}\",\"carrier\":\"${_esc_carrier}\"}" + # WAN mapping in canonical GL format: + # modem_0001 = cellular (Eyeride 5G) + # wan = ethernet/fiber (AT&T Uverse) + _wan_modem="$WAN2_IFACE" # which physical iface maps to modem_0001 + _wan_ether="$WAN1_IFACE" # which physical iface maps to wan + + # Check if roles were swapped by auto-detection + if [ "$_cell_iface" != "$WAN2_IFACE" ]; then + # WAN1 is cellular, WAN2 is ethernet + _modem_json="$_cell_json" + _ether_json="$_eth_json" + else + # WAN2 is cellular, WAN1 is ethernet (normal) + _modem_json="$_cell_json" + _ether_json="$_eth_json" fi # Active WAN: determine from routing table (default route interface) - sel_primary="modem_0001" + sel_primary="wan" # default: fiber/ethernet is primary default_iface=$(ip route show default 2>/dev/null | awk '{print $5}' | head -1) - if [ "$default_iface" = "$WAN2_IFACE" ]; then - sel_primary="wan" + if [ "$default_iface" = "$_cell_iface" ]; then + sel_primary="modem_0001" # cellular is the active path fi # Starlink — detect if present sl_lat="null" sl_dl="null" sl_obs="0" sl_outage="0" - # Starlink is typically on a USB ethernet adapter or separate VLAN if ip link show 2>/dev/null | grep -qi starlink; then - # Basic Starlink check — if interface exists, try to get metrics - sl_iface=$(ip link show 2>/dev/null | grep -i starlink | head -1 | awk -F': ' '{print $2}' | awk '{print $1}') + sl_iface=$(ip link show 2>/dev/null | grep -i starlink | head -1 | awk -F": " '{print $2}' | awk '{print $1}') if [ -n "$sl_iface" ]; then sl_ping=$(_ping_iface "$sl_iface") sl_lat=$(echo "$sl_ping" | awk '{print $1}') @@ -362,7 +459,7 @@ telemetry_collect() { printf '{"device_id":"%s","timestamp":%d,"uptime":%d,"version":"%s","platform":"synology","sel_primary":"%s","active_wan":"%s","gps":{"lat":%s,"lon":%s,"fix":%s},"wan":{"modem_0001":%s,"wan":%s},"sim_slot":null,"starlink":{"latency_ms":%s,"dl_bps":%s,"obstructed":%s,"outage":%s},"state":%s}\n' \ "${esc_id}" "${now:-0}" "${uptime_s:-0}" "${esc_ver}" "${esc_sel}" "${esc_sel}" \ "${gps_lat}" "${gps_lon}" "${gps_fix}" \ - "${wan1_json}" "${wan2_json}" \ + "${_modem_json}" "${_ether_json}" \ "${sl_lat}" "${sl_dl}" "${sl_obs}" "${sl_outage}" \ "${state}" }