Files
kit-busrouter/syno-balance/bin/smartwan-adapter.sh
T
allen f15ac69925 feat: kit-busrouter v2.0 — complete Synology + GL fleet router platform
Includes:
- package/ GL-XE3000 kit-busrouter (opkg)
- scripts/provision.sh (GL) and provision-synology.sh (Synology)
- syno-balance/ — new WAN balancer replacing aiwanbal (SmartWAN adapter)
- kit-connect/ — unified connectivity SPK (Tailscale + reverse SSH)
- docs/deployment/synology-rt2600ac-checklist.md — 62-point checklist
- docs/provisioning/device-identity.md — fleet identity spec
- docs/pilot/checklist.md — field pilot validation
- x4078_20260721.dss — reference config backup

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 00:07:14 +00:00

118 lines
4.7 KiB
Bash
Executable File

#!/bin/sh
# smartwan-adapter.sh — Read SmartWAN state and write dw_weight_ratio.
# NEVER calls loadbalance lb-enable. NEVER calls loadbalance at all.
# Rides entirely on top of SmartWAN — does not own routing.
#
# Public API:
# sw_read_state read SmartWAN mode, interfaces, current weights
# sw_read_wan_state <iface> read per-WAN state: operstate, gateway, weight
# sw_write_weights <w1> <w2> write dw_weight_ratio (50=balanced, 20/80=biased)
# sw_signal_reload touch SmartWAN to re-read config (light touch, no conntrack flush)
#
# Config file: /usr/syno/etc/smartwan/smartwan.conf
# Weight field: dw_weight_ratio = <0-100> (WAN1 share %, WAN2 = 100-WAN1)
# - dw_weight_ratio appears twice in the conf (once for each ifname section)
# - We update BOTH occurrences
# - 50/50 = balanced, 70 = 70% WAN1 / 30% WAN2
SMARTWAN_CONF="/usr/syno/etc/smartwan/smartwan.conf"
STATE_DIR="${STATE_DIR:-/tmp/syno-balance}"
LOG_TAG="${LOG_TAG:-syno-balance}"
log() { logger -t "$LOG_TAG" -p local0.warn "$*"; }
warn() { logger -t "$LOG_TAG" -p local0.warn "WARNING: $*"; }
# ── Read SmartWAN global state ──────────────────────────────────────
sw_read_state() {
[ -f "$SMARTWAN_CONF" ] || { warn "smartwan.conf not found"; return 1; }
. "$SMARTWAN_CONF" 2>/dev/null
echo "MODE=${smartwan_mode:-unknown}"
echo "WAN1_IFNAME=${smartwan_ifname_1:-wan}"
echo "WAN2_IFNAME=${smartwan_ifname_2:-lan1}"
echo "WAN1_WEIGHT=${dw_weight_ratio:-50}"
}
# ── Read a single WAN's runtime state ───────────────────────────────
# Returns: STATE=up|down GW=x.x.x.x WEIGHT=N
sw_read_wan_state() {
local iface="$1"
[ -n "$iface" ] || return 1
# Physical link state
local operstate="down"
operstate=$(cat "/sys/class/net/${iface}/operstate" 2>/dev/null || echo "unknown")
# Gateway from routing table
local gw=""
gw=$(ip route show dev "$iface" 2>/dev/null | grep default | awk '{print $3}' | head -1)
[ -z "$gw" ] && gw=$(ip route show dev "$iface" 2>/dev/null | awk '/via/ {print $3}' | head -1)
# Current weight from smartwan.conf
local weight=0
[ -f "$SMARTWAN_CONF" ] && . "$SMARTWAN_CONF" 2>/dev/null
weight="${dw_weight_ratio:-50}"
echo "IFACE=${iface} STATE=${operstate} GW=${gw:-none} WEIGHT=${weight}"
}
# ── Write dw_weight_ratio for both WANs ─────────────────────────────
# $1: WAN1 weight (0-100, WAN2 = 100-WAN1)
# Sets dw_weight_ratio in smartwan.conf for both ifname sections.
# Does NOT call loadbalance. SmartWAN re-reads on its own cycle.
sw_write_weights() {
local w1="$1"
case "$w1" in
''|*[!0-9]*) warn "invalid weight: $w1"; return 1 ;;
esac
[ "$w1" -lt 0 ] && w1=0
[ "$w1" -gt 100 ] && w1=100
local current=50
[ -f "$SMARTWAN_CONF" ] && . "$SMARTWAN_CONF" 2>/dev/null
current="${dw_weight_ratio:-50}"
# Only write if changed by >= 10 (hysteresis via lib-decide.sh)
local diff=$(( w1 > current ? w1 - current : current - w1 ))
if [ "$diff" -lt 10 ]; then
return 0 # within hysteresis band — no-op
fi
# Write to smartwan.conf using sed — safe, atomic-ish
# SmartWAN has dw_weight_ratio on both ifname sections
if [ -f "$SMARTWAN_CONF" ]; then
sed -i "s/^dw_weight_ratio=.*/dw_weight_ratio=${w1}/" "$SMARTWAN_CONF" 2>/dev/null
fi
log "smartwan: dw_weight_ratio ${current} -> ${w1} (WAN1=${w1}% WAN2=$((100-w1))%)"
mkdir -p "$STATE_DIR"
echo "$w1" > "$STATE_DIR/sw_weight_written"
echo "$(date +%s)" > "$STATE_DIR/sw_weight_time"
}
# ── Light touch — signal SmartWAN to re-read config ────────────────
# Uses synoservice if available. NEVER calls loadbalance lb-enable.
sw_signal_reload() {
# SRM 1.3+ has synoservice to restart individual services
if command -v synoservice >/dev/null 2>&1; then
synoservice --restart smartwan 2>/dev/null && \
{ log "smartwan: light-reload via synoservice"; return 0; }
fi
# Fallback: just touch the config — SmartWAN polls it
touch "$SMARTWAN_CONF" 2>/dev/null
log "smartwan: config touched for polling re-read"
return 0
}
# ── Check if SmartWAN is in load-balance mode ───────────────────────
sw_is_lb_mode() {
[ -f "$SMARTWAN_CONF" ] || return 1
local mode=""
mode=$(grep '^smartwan_mode=' "$SMARTWAN_CONF" 2>/dev/null | cut -d= -f2)
case "$mode" in
loadbalance|loadbalancing_failover) return 0 ;;
*) return 1 ;;
esac
}