- Add Instances tab to LuCI Streamlit dashboard - RPCD backend: list/add/remove/enable/disable instances - API module: instance management methods - UI: Instance table with status, port, enable/disable/remove actions - Add Instance form with app selector and auto port assignment - Apply & Restart button to apply instance changes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
178 lines
5.7 KiB
Bash
178 lines
5.7 KiB
Bash
#!/bin/sh
|
|
# SecuBox Network Health Monitor
|
|
# Detects CRC errors, link flapping, and interface issues
|
|
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
DMESG_LINES=500
|
|
FLAP_THRESHOLD=5 # Number of link changes to consider flapping
|
|
CRC_THRESHOLD=10 # CRC errors to consider problematic
|
|
|
|
check_interface_health() {
|
|
local iface="$1"
|
|
local status="ok"
|
|
local issues=""
|
|
local crc_count=0
|
|
local link_changes=0
|
|
local current_state="unknown"
|
|
|
|
# Get current link state
|
|
if [ -d "/sys/class/net/$iface" ]; then
|
|
current_state=$(cat /sys/class/net/$iface/operstate 2>/dev/null || echo "unknown")
|
|
fi
|
|
|
|
# Count CRC errors from dmesg (last N lines)
|
|
crc_count=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface.*crc error" 2>/dev/null || echo 0)
|
|
|
|
# Count link state changes from dmesg
|
|
link_up=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface: Link is Up" 2>/dev/null || echo 0)
|
|
link_down=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface: Link is Down" 2>/dev/null || echo 0)
|
|
link_changes=$((link_up + link_down))
|
|
|
|
# Determine status
|
|
if [ "$crc_count" -ge "$CRC_THRESHOLD" ]; then
|
|
status="critical"
|
|
issues="${issues}CRC errors detected ($crc_count); "
|
|
fi
|
|
|
|
if [ "$link_changes" -ge "$FLAP_THRESHOLD" ]; then
|
|
if [ "$status" = "ok" ]; then
|
|
status="warning"
|
|
fi
|
|
issues="${issues}Link flapping detected ($link_changes changes); "
|
|
fi
|
|
|
|
# Get interface stats
|
|
local rx_errors=0 tx_errors=0 rx_dropped=0 tx_dropped=0
|
|
if [ -f "/sys/class/net/$iface/statistics/rx_errors" ]; then
|
|
rx_errors=$(cat /sys/class/net/$iface/statistics/rx_errors)
|
|
tx_errors=$(cat /sys/class/net/$iface/statistics/tx_errors)
|
|
rx_dropped=$(cat /sys/class/net/$iface/statistics/rx_dropped)
|
|
tx_dropped=$(cat /sys/class/net/$iface/statistics/tx_dropped)
|
|
fi
|
|
|
|
if [ "$rx_errors" -gt 1000 ] || [ "$tx_errors" -gt 1000 ]; then
|
|
if [ "$status" = "ok" ]; then
|
|
status="warning"
|
|
fi
|
|
issues="${issues}High error count (rx:$rx_errors tx:$tx_errors); "
|
|
fi
|
|
|
|
# Output JSON for this interface
|
|
json_add_object "$iface"
|
|
json_add_string "status" "$status"
|
|
json_add_string "state" "$current_state"
|
|
json_add_int "crc_errors" "$crc_count"
|
|
json_add_int "link_changes" "$link_changes"
|
|
json_add_int "rx_errors" "$rx_errors"
|
|
json_add_int "tx_errors" "$tx_errors"
|
|
json_add_int "rx_dropped" "$rx_dropped"
|
|
json_add_int "tx_dropped" "$tx_dropped"
|
|
json_add_string "issues" "${issues%%; }"
|
|
json_close_object
|
|
}
|
|
|
|
get_network_health() {
|
|
json_init
|
|
json_add_string "timestamp" "$(date -Iseconds)"
|
|
json_add_object "interfaces"
|
|
|
|
# Check all physical interfaces
|
|
for iface in /sys/class/net/eth* /sys/class/net/wan* /sys/class/net/lan*; do
|
|
[ -d "$iface" ] || continue
|
|
iface_name=$(basename "$iface")
|
|
# Skip virtual interfaces (must have device link)
|
|
[ -d "$iface/device" ] || continue
|
|
check_interface_health "$iface_name"
|
|
done
|
|
|
|
json_close_object
|
|
|
|
# Overall status
|
|
local overall="healthy"
|
|
local critical_count=0
|
|
local warning_count=0
|
|
|
|
# Re-scan for overall status
|
|
for iface in /sys/class/net/eth* /sys/class/net/wan* /sys/class/net/lan*; do
|
|
[ -d "$iface" ] || continue
|
|
[ -d "$iface/device" ] || continue
|
|
iface_name=$(basename "$iface")
|
|
|
|
crc=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface_name.*crc error" 2>/dev/null || echo 0)
|
|
if [ "$crc" -ge "$CRC_THRESHOLD" ]; then
|
|
critical_count=$((critical_count + 1))
|
|
fi
|
|
|
|
link_up=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface_name: Link is Up" 2>/dev/null || echo 0)
|
|
link_down=$(dmesg | tail -n $DMESG_LINES | grep -c "$iface_name: Link is Down" 2>/dev/null || echo 0)
|
|
if [ $((link_up + link_down)) -ge "$FLAP_THRESHOLD" ]; then
|
|
warning_count=$((warning_count + 1))
|
|
fi
|
|
done
|
|
|
|
if [ "$critical_count" -gt 0 ]; then
|
|
overall="critical"
|
|
elif [ "$warning_count" -gt 0 ]; then
|
|
overall="warning"
|
|
fi
|
|
|
|
json_add_string "overall" "$overall"
|
|
json_add_int "critical_interfaces" "$critical_count"
|
|
json_add_int "warning_interfaces" "$warning_count"
|
|
|
|
# Add recommendations if issues found
|
|
if [ "$overall" != "healthy" ]; then
|
|
json_add_array "recommendations"
|
|
if [ "$critical_count" -gt 0 ]; then
|
|
json_add_string "" "Check/replace Ethernet cables on affected interfaces"
|
|
json_add_string "" "Try different port on switch/modem"
|
|
json_add_string "" "Inspect RJ45 connectors for damage"
|
|
fi
|
|
if [ "$warning_count" -gt 0 ]; then
|
|
json_add_string "" "Monitor link stability"
|
|
json_add_string "" "Check for EMI interference near cables"
|
|
fi
|
|
json_close_array
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
get_interface_detail() {
|
|
local iface="$1"
|
|
|
|
if [ ! -d "/sys/class/net/$iface" ]; then
|
|
echo '{"error": "Interface not found"}'
|
|
return 1
|
|
fi
|
|
|
|
json_init
|
|
json_add_string "interface" "$iface"
|
|
json_add_string "state" "$(cat /sys/class/net/$iface/operstate 2>/dev/null)"
|
|
json_add_string "mac" "$(cat /sys/class/net/$iface/address 2>/dev/null)"
|
|
json_add_int "mtu" "$(cat /sys/class/net/$iface/mtu 2>/dev/null)"
|
|
|
|
# Recent dmesg entries for this interface
|
|
json_add_array "recent_events"
|
|
dmesg | tail -n 100 | grep "$iface" | tail -n 10 | while read line; do
|
|
json_add_string "" "$line"
|
|
done
|
|
json_close_array
|
|
|
|
json_dump
|
|
}
|
|
|
|
case "$1" in
|
|
status|health)
|
|
get_network_health
|
|
;;
|
|
detail)
|
|
get_interface_detail "$2"
|
|
;;
|
|
*)
|
|
echo "Usage: $0 {status|health|detail <interface>}"
|
|
exit 1
|
|
;;
|
|
esac
|