f15ac69925
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>
118 lines
4.7 KiB
Bash
Executable File
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
|
|
}
|