Add web GUI, docs, scripts, and 5G router config

- Web app (Flask): status, config, firewall, logs, users, restart
- Docs: AT commands, deploy, DNS, quickstart, web GUI
- Scripts: connect, deploy, diag, healthcheck, modem-status, speedtest, status, troubleshoot
- Init and iptables: 5g-router, 5g-webgui, rules.v4
- CHANGELOG, TODO, REVISION; config and README updates
This commit is contained in:
nearxos
2026-02-02 09:38:23 +02:00
parent 1136a332b5
commit 160ad641ce
46 changed files with 4320 additions and 40 deletions

168
scripts/connect-5g.sh Normal file
View File

@@ -0,0 +1,168 @@
#!/bin/sh
# Alpine 5G Router connection script (run by 5g-router service or manually)
# Uses /etc/5g-router.conf if present. Supports watchdog and failover.
# Rev: 2 (see REVISION in repo root)
set -e
CONFIG="/etc/5g-router.conf"
LOG_FILE="/var/log/5g-router.log"
# Defaults
AT_PORT="/dev/ttyUSB1"
APN="internet"
WAN_IF="eth1"
LAN_IF="eth0.100"
FAILOVER_ENABLED="no"
FAILOVER_IF="eth0"
FAILOVER_METRIC="202"
WATCHDOG_INTERVAL="0"
LOG_SIGNAL="yes"
WEAK_SIGNAL_CSQ="10"
[ -f "$CONFIG" ] && . "$CONFIG"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" 2>/dev/null || echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"; }
# Fix broken /dev/ttyUSB* node (if it is a regular file instead of char device - e.g. after modem reconnect)
fix_ttyusb_if_needed() {
case "$AT_PORT" in
/dev/ttyUSB[0-9]*) ;;
*) return 0 ;;
esac
[ ! -e "$AT_PORT" ] && return 0
if [ -f "$AT_PORT" ] && [ ! -c "$AT_PORT" ]; then
_num="${AT_PORT#/dev/ttyUSB}"
log "Fixing $AT_PORT (was regular file, recreating as char device)"
rm -f "$AT_PORT"
mknod "$AT_PORT" c 188 "$_num"
chmod 660 "$AT_PORT"
chown root:dialout "$AT_PORT" 2>/dev/null || true
fi
}
get_at_response() {
_cmd="$1"
_wait="${2:-2}"
timeout $((_wait + 4)) sh -c "
cat $AT_PORT 2>/dev/null &
_pid=\$!
sleep 0.5
echo \"${_cmd}\r\" > $AT_PORT
sleep $_wait
kill \$_pid 2>/dev/null
" 2>&1
}
wait_for_modem() {
log "Waiting for modem AT port..."
for _i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
fix_ttyusb_if_needed
if [ -c "$AT_PORT" ]; then
log "Modem AT port available"
return 0
fi
sleep 2
done
log "Modem AT port not available"
return 1
}
check_usb_mode() {
if lsusb 2>/dev/null | grep -q "0e8d:7127"; then
log "WARN: Modem is in Mode 41 (7127). AT commands may not work. Reboot or power-cycle modem."
return 1
fi
return 0
}
do_connect() {
# eth1 (RNDIS) does NOT provide DHCP. We get IP (and DNS) only via AT commands
# and configure the interface manually. Do not run dhclient on eth1.
check_usb_mode || return 1
# Modem can take 35s to respond after boot; use 5s so service (started at boot) gets OK
get_at_response "AT" 5 | grep -q "OK" || { log "AT not OK (try longer wait or different AT port)"; return 1; }
# Signal (optional log)
_resp=$(get_at_response "AT+CSQ" 1)
_csq=$(echo "$_resp" | grep "+CSQ:" | grep -oE '[0-9]+' | head -1)
[ -n "$_csq" ] && [ "$LOG_SIGNAL" = "yes" ] && echo "$(date -Iseconds) CSQ=$_csq" >> "$LOG_FILE" 2>/dev/null || true
[ -n "$_csq" ] && [ -n "$WEAK_SIGNAL_CSQ" ] && [ "$WEAK_SIGNAL_CSQ" -gt 0 ] && [ "$_csq" -lt "$WEAK_SIGNAL_CSQ" ] && log "WARN weak signal CSQ=$_csq"
log "Configuring APN: $APN"
get_at_response "AT+CGDCONT=1,\"IP\",\"$APN\"" 2 | grep -q "OK" || true
log "Activating PDP context..."
get_at_response "AT+CGACT=1,1" 3 | grep -qE "(OK|CGEV)" || true
_ip=""
for _retry in 1 2 3 4 5; do
_resp=$(get_at_response "AT+CGPADDR=1" 2)
_ip=$(echo "$_resp" | grep "+CGPADDR:" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1)
[ -n "$_ip" ] && break
[ $_retry -lt 5 ] && log "No IP yet, retry $_retry/5 in 3s..." && sleep 3
done
[ -z "$_ip" ] && log "Could not get modem IP (check signal/registration: AT+CSQ, AT+CEREG?)" && return 1
# Get DNS from modem (AT+CGCONTRDP=1); modem does not provide DHCP, only IP/DNS via AT
_dns1=""; _dns2=""
_contrdp=$(get_at_response "AT+CGCONTRDP=1" 2)
_allips=""
for _a in $(echo "$_contrdp" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'); do
[ "$_a" = "$_ip" ] && continue
_allips="${_allips} ${_a}"
done
for _a in $_allips; do [ -z "$_a" ] && continue; _dns2="$_dns1"; _dns1="$_a"; done
[ -z "$_dns1" ] && [ -n "$DNS_SERVERS" ] && _dns1=$(echo "$DNS_SERVERS" | cut -d',' -f1 | tr -d ' ') && _dns2=$(echo "$DNS_SERVERS" | cut -d',' -f2 | tr -d ' ')
if [ -n "$_dns1" ]; then
: > /etc/resolv.conf
echo "nameserver $_dns1" >> /etc/resolv.conf
[ -n "$_dns2" ] && echo "nameserver $_dns2" >> /etc/resolv.conf
log "DNS set (from modem or config)"
fi
log "Configuring $WAN_IF with IP $_ip (no DHCP - from AT+CGPADDR=1)"
ip link set "$WAN_IF" up 2>/dev/null || true
ip addr flush dev "$WAN_IF" 2>/dev/null || true
ip addr add "$_ip/32" dev "$WAN_IF"
ip route add default dev "$WAN_IF" metric 50 2>/dev/null || true
log "Interface configured"
log "Setting up NAT..."
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -C POSTROUTING -o "$WAN_IF" -j MASQUERADE 2>/dev/null || iptables -t nat -A POSTROUTING -o "$WAN_IF" -j MASQUERADE
iptables -C FORWARD -i "$LAN_IF" -o "$WAN_IF" -j ACCEPT 2>/dev/null || iptables -A FORWARD -i "$LAN_IF" -o "$WAN_IF" -j ACCEPT
iptables -C FORWARD -i "$WAN_IF" -o "$LAN_IF" -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || iptables -A FORWARD -i "$WAN_IF" -o "$LAN_IF" -m state --state RELATED,ESTABLISHED -j ACCEPT
log "NAT configured"
return 0
}
do_failover() {
[ "$FAILOVER_ENABLED" != "yes" ] && return 0
# Ensure there is a default route via failover interface (higher metric so 5G wins when up)
ip route add default dev "$FAILOVER_IF" metric "$FAILOVER_METRIC" 2>/dev/null || true
}
# Main
run_once() {
wait_for_modem || { do_failover; return 1; }
if do_connect; then
log "5G connection successful!"
return 0
else
do_failover
return 1
fi
}
if [ -n "$WATCHDOG_INTERVAL" ] && [ "$WATCHDOG_INTERVAL" -gt 0 ]; then
log "Starting 5G connection (watchdog every ${WATCHDOG_INTERVAL}s)..."
while true; do
run_once || true
sleep "$WATCHDOG_INTERVAL"
done
else
log "Starting 5G connection..."
run_once || true
log "Holding process for service (stop with: service 5g-router stop)."
exec sleep infinity
fi

47
scripts/deploy.sh Executable file
View File

@@ -0,0 +1,47 @@
#!/bin/sh
# Alpine 5G Router deploy/update device from this repo
# Run from repo root: ./scripts/deploy.sh [user@host]
# Or: TARGET=root@192.168.1.1 ./scripts/deploy.sh
#
# Copies etc/, scripts/, web/, configure_fm350_5g.sh to the device,
# runs install.sh there, then restarts 5g-router and 5g-webgui.
set -e
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
TARGET="${1:-${TARGET:-root@10.130.60.121}}"
REMOTE_DIR="/tmp/Alpine_5G"
SSH_OPTS="${SSH_OPTS:--o ConnectTimeout=10}"
echo "Deploying Alpine 5G Router to $TARGET"
echo " Repo root: $ROOT"
echo " Remote dir: $REMOTE_DIR"
echo ""
# Copy files to device
echo "[1/3] Copying files to $TARGET:$REMOTE_DIR ..."
ssh $SSH_OPTS "$TARGET" "mkdir -p $REMOTE_DIR"
scp $SSH_OPTS -r "$ROOT/etc" "$ROOT/scripts" "$ROOT/configure_fm350_5g.sh" "$TARGET:$REMOTE_DIR/"
if [ -d "$ROOT/web" ] && [ -f "$ROOT/web/app.py" ]; then
scp $SSH_OPTS -r "$ROOT/web" "$TARGET:$REMOTE_DIR/"
fi
echo " Done."
echo ""
# Run install on device
echo "[2/3] Running install.sh on device ..."
ssh $SSH_OPTS "$TARGET" "cd $REMOTE_DIR && chmod +x scripts/*.sh 2>/dev/null; chmod +x etc/init.d/* 2>/dev/null; ./scripts/install.sh"
echo ""
# Restart services to pick up changes
echo "[3/3] Restarting services ..."
ssh $SSH_OPTS "$TARGET" "
service 5g-router restart 2>/dev/null || true
service 5g-webgui restart 2>/dev/null || true
echo ' Done.'
"
echo ""
echo "Deployment complete. Device: $TARGET"
echo " Web GUI: http://<device-ip>:5000"
echo " SSH: ssh $TARGET"

92
scripts/diag-at-port.sh Normal file
View File

@@ -0,0 +1,92 @@
#!/bin/sh
# Alpine 5G Router AT port diagnostic (run on device to debug “No modem AT data”)
# Rev: 1 (see REVISION in repo root)
# Usage: ./diag-at-port.sh or /usr/local/bin/diag-at-port.sh
CONFIG="/etc/5g-router.conf"
AT_PORT="${AT_PORT:-/dev/ttyUSB1}"
[ -f "$CONFIG" ] && . "$CONFIG"
echo "=== Alpine 5G Router AT port diagnostic ==="
echo ""
# 1) User and permissions
echo "--- User & groups ---"
echo "User: $(id -un) (uid=$(id -u) gid=$(id -g))"
echo "Groups: $(id -Gn)"
if id -Gn | grep -q dialout; then
echo "dialout: YES (can access serial ports)"
else
echo "dialout: NO add user to dialout: adduser <user> dialout"
fi
echo ""
# 2) Serial devices
echo "--- Serial devices ---"
for d in /dev/ttyUSB* /dev/ttyACM*; do
[ -e "$d" ] || continue
ls -la "$d" 2>/dev/null
done
if ! ls /dev/ttyUSB* /dev/ttyACM* 2>/dev/null | grep -q .; then
echo "No /dev/ttyUSB* or /dev/ttyACM* found. Modem may not be in USB mode 40 or not bound."
fi
echo ""
# 3) Modem USB
echo "--- Modem (lsusb) ---"
lsusb 2>/dev/null | grep -i fibocom || echo "No Fibocom device in lsusb"
lsusb 2>/dev/null | grep -i "0e8d" || true
echo ""
# 4) Config
echo "--- Config ---"
echo "AT_PORT from config: $AT_PORT"
if [ -c "$AT_PORT" ]; then
echo " -> exists and is a character device"
else
echo " -> missing or not a character device"
fi
echo ""
# 5) Raw AT probe on each ttyUSB
echo "--- Raw AT probe (send AT, show response) ---"
for port in /dev/ttyUSB0 /dev/ttyUSB1 /dev/ttyUSB2 /dev/ttyUSB3; do
[ -c "$port" ] || continue
echo "Port $port:"
out=$(timeout 4 sh -c "
cat $port 2>/dev/null &
_p=\$!
sleep 0.3
echo -e 'AT\r' > $port 2>/dev/null
sleep 1
kill \$_p 2>/dev/null
" 2>/dev/null)
if echo "$out" | grep -q 'OK'; then
echo " -> OK (modem responded)"
else
echo " -> no OK in response (raw below)"
fi
echo "$out" | head -5 | sed 's/^/ /'
echo ""
done
if ! ls /dev/ttyUSB* 2>/dev/null | grep -q .; then
echo "No ttyUSB devices to probe."
fi
echo ""
# 6) modem-status-at.sh if present
echo "--- modem-status-at.sh ---"
if [ -x "/usr/local/bin/modem-status-at.sh" ]; then
echo "Running: /usr/local/bin/modem-status-at.sh"
out=$(timeout 20 /usr/local/bin/modem-status-at.sh 2>&1)
code=$?
echo "Exit code: $code"
echo "$out" | head -15
if [ "$code" != 0 ] || echo "$out" | grep -q '^{}$'; then
echo "... (no modem JSON or script failed)"
fi
else
echo "Not found at /usr/local/bin/modem-status-at.sh (install with scripts/install.sh)"
fi
echo ""
echo "=== End diagnostic ==="

131
scripts/diag-modem-up.sh Normal file
View File

@@ -0,0 +1,131 @@
#!/bin/sh
# Alpine 5G Router modem/WAN diagnostic (run on device to debug “modem not up”)
# Rev: 1 (see REVISION in repo root)
# Usage: ./diag-modem-up.sh or /usr/local/bin/diag-modem-up.sh
CONFIG="/etc/5g-router.conf"
WAN_IF="${WAN_IF:-eth1}"
AT_PORT="${AT_PORT:-/dev/ttyUSB1}"
LOG_FILE="/var/log/5g-router.log"
[ -f "$CONFIG" ] && . "$CONFIG"
echo "=== Alpine 5G Router modem/WAN diagnostic ==="
echo ""
# 1) Service
echo "--- 5g-router service ---"
if command -v rc-service >/dev/null 2>&1; then
rc-service 5g-router status 2>&1 || true
else
echo "OpenRC not found; service status skipped"
fi
echo ""
# 2) Config
echo "--- Config ($CONFIG) ---"
if [ -f "$CONFIG" ]; then
echo "WAN_IF=$WAN_IF AT_PORT=$AT_PORT"
grep -E '^APN=|^WAN_IF=|^AT_PORT=' "$CONFIG" 2>/dev/null || true
else
echo "Config file not found (using defaults)"
fi
echo ""
# 3) Modem USB
echo "--- Modem (lsusb) ---"
MODEM_LINE=$(lsusb 2>/dev/null | grep -i fibocom || lsusb 2>/dev/null | grep "0e8d" || true)
if [ -n "$MODEM_LINE" ]; then
echo "$MODEM_LINE"
if echo "$MODEM_LINE" | grep -q "7127"; then
echo " -> Mode 41 (0e8d:7127): AT port may not work; use Mode 40 (0e8d:7126)"
elif echo "$MODEM_LINE" | grep -q "7126"; then
echo " -> Mode 40 (RNDIS): AT port should be available"
fi
else
echo "No Fibocom / 0e8d device modem not seen by USB"
fi
echo ""
# 4) WAN interface
echo "--- WAN interface ($WAN_IF) ---"
if ip link show "$WAN_IF" >/dev/null 2>&1; then
ip link show "$WAN_IF"
WAN_STATE=$(ip link show "$WAN_IF" 2>/dev/null | grep -oE 'state [A-Z]+' | awk '{print $2}')
WAN_IP=$(ip -4 addr show "$WAN_IF" 2>/dev/null | grep -oE 'inet [0-9.]+' | awk '{print $2}')
echo " State: $WAN_STATE IP: ${WAN_IP:-none}"
if [ "$WAN_STATE" = "DOWN" ]; then
echo " -> Interface is DOWN (connect-5g.sh may have failed or not run)"
fi
if [ -z "$WAN_IP" ]; then
echo " -> No IP (PDP not activated or modem did not assign address)"
fi
else
echo " Interface $WAN_IF does not exist (wrong name or modem not bound?)"
fi
echo ""
# 5) Default route
echo "--- Default route ---"
DEFAULT=$(ip route show default 2>/dev/null | head -1)
if [ -n "$DEFAULT" ]; then
echo "$DEFAULT"
DEF_DEV=$(echo "$DEFAULT" | awk '{print $3}')
if [ "$DEF_DEV" != "$WAN_IF" ]; then
echo " -> Default is via $DEF_DEV, not $WAN_IF (5G may be down or failover active)"
fi
else
echo " No default route"
fi
echo ""
# 6) AT port
echo "--- AT port ($AT_PORT) ---"
if [ -c "$AT_PORT" ]; then
echo " Exists (character device)"
out=$(timeout 4 sh -c "
cat $AT_PORT 2>/dev/null &
_p=\$!
sleep 0.3
echo -e 'AT\r' > $AT_PORT 2>/dev/null
sleep 1
kill \$_p 2>/dev/null
" 2>/dev/null)
if echo "$out" | grep -q 'OK'; then
echo " AT response: OK"
else
echo " AT response: no OK (raw: $(echo "$out" | head -1))"
fi
else
echo " Not available (missing or not a char device) connect-5g.sh waits for this"
for p in /dev/ttyUSB0 /dev/ttyUSB1 /dev/ttyUSB2; do
[ -c "$p" ] && echo " Found: $p"
done
fi
echo ""
# 7) Last log lines
echo "--- Last 20 lines of $LOG_FILE ---"
if [ -f "$LOG_FILE" ]; then
tail -20 "$LOG_FILE" 2>/dev/null | sed 's/^/ /'
else
echo " Log file not found"
fi
echo ""
# 8) Connectivity
echo "--- Connectivity (ping 8.8.8.8) ---"
if ping -c 1 -W 3 8.8.8.8 >/dev/null 2>&1; then
echo " Reachable (modem/route may be OK)"
else
echo " Not reachable (WAN down or no default via 5G)"
fi
echo ""
echo "=== End diagnostic ==="
echo ""
echo "Typical fixes:"
echo " - Modem not in lsusb: power-cycle modem or USB; check cable"
echo " - Mode 41 (7127): reboot modem or run AT+GTUSBMODE=40 then AT+CFUN=1,1"
echo " - AT port missing: wait for modem to expose ttyUSB (can take 30s after boot)"
echo " - AT no OK: wrong port set AT_PORT in $CONFIG (e.g. /dev/ttyUSB0)"
echo " - No modem IP: check APN in $CONFIG; check SIM/network registration"
echo " - Start connection: service 5g-router start or /usr/local/bin/connect-5g.sh"

44
scripts/healthcheck-5g.sh Normal file
View File

@@ -0,0 +1,44 @@
#!/bin/sh
# Alpine 5G Router health check for monitoring (Nagios, Uptime Kuma, etc.)
# Exit 0 = OK, 1 = 5G down or no connectivity
# Usage: healthcheck-5g.sh [--ping-only]
# Rev: 1 (see REVISION in repo root)
CONFIG="/etc/5g-router.conf"
WAN_IF="${WAN_IF:-eth1}"
[ -f "$CONFIG" ] && . "$CONFIG"
PING_TARGET="${PING_TARGET:-8.8.8.8}"
# Check default route goes via 5G
def_if=$(ip route show default 2>/dev/null | awk '{print $5}' | head -1)
if [ "$def_if" != "$WAN_IF" ]; then
echo "CRITICAL: default route not via $WAN_IF (got: $def_if)"
exit 1
fi
# Check WAN has an address
wan_ip=$(ip -4 addr show "$WAN_IF" 2>/dev/null | grep -oE 'inet [0-9.]+' | awk '{print $2}')
if [ -z "$wan_ip" ]; then
echo "CRITICAL: no IP on $WAN_IF"
exit 1
fi
if [ "$1" = "--ping-only" ]; then
if ping -c 1 -W 3 "$PING_TARGET" >/dev/null 2>&1; then
echo "OK: ping $PING_TARGET"
exit 0
else
echo "CRITICAL: ping $PING_TARGET failed"
exit 1
fi
fi
# Full check: route + ping
if ping -c 2 -W 5 "$PING_TARGET" >/dev/null 2>&1; then
echo "OK: 5G up, ping $PING_TARGET"
exit 0
else
echo "CRITICAL: 5G route OK but ping $PING_TARGET failed"
exit 1
fi

88
scripts/install.sh Normal file
View File

@@ -0,0 +1,88 @@
#!/bin/sh
# Alpine 5G Router install/deploy scripts and config to this device
# Run from repo root: ./scripts/install.sh
# Or from scripts/: ./install.sh (uses script dir to find repo root)
# Rev: 1 (see REVISION in repo root)
set -e
# Repo root (directory containing etc/ and scripts/)
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
ETC="$ROOT/etc"
SCRIPTS="$ROOT/scripts"
BIN="/usr/local/bin"
INITD="/etc/init.d"
IPTABLES_SAVE="/etc/iptables"
CONFIG="/etc/5g-router.conf"
CONFIG_EXAMPLE="$ETC/5g-router.conf.example"
LOG_DIR="/var/log"
echo "Installing Alpine 5G Router from $ROOT"
# Config: copy example if no config exists
if [ ! -f "$CONFIG" ]; then
cp "$CONFIG_EXAMPLE" "$CONFIG"
echo "Created $CONFIG please edit APN and interfaces if needed."
else
echo "Keeping existing $CONFIG"
fi
# Scripts
install -m 755 "$SCRIPTS/connect-5g.sh" "$BIN/connect-5g.sh"
install -m 755 "$SCRIPTS/status-5g.sh" "$BIN/status-5g.sh"
install -m 755 "$SCRIPTS/modem-status-at.sh" "$BIN/modem-status-at.sh"
install -m 755 "$SCRIPTS/healthcheck-5g.sh" "$BIN/healthcheck-5g.sh"
install -m 755 "$SCRIPTS/speedtest-5g.sh" "$BIN/speedtest-5g.sh"
install -m 755 "$SCRIPTS/rotate-5g-log.sh" "$BIN/rotate-5g-log.sh"
install -m 755 "$SCRIPTS/diag-at-port.sh" "$BIN/diag-at-port.sh"
install -m 755 "$SCRIPTS/diag-modem-up.sh" "$BIN/diag-modem-up.sh"
install -m 755 "$SCRIPTS/troubleshoot-5g.sh" "$BIN/troubleshoot-5g.sh"
echo "Installed scripts to $BIN"
# Optional: configure_fm350_5g.sh for manual runs
if [ -f "$ROOT/configure_fm350_5g.sh" ]; then
install -m 755 "$ROOT/configure_fm350_5g.sh" "$BIN/configure_fm350_5g.sh"
echo "Installed configure_fm350_5g.sh"
fi
# OpenRC service
install -m 755 "$ETC/init.d/5g-router" "$INITD/5g-router"
echo "Installed $INITD/5g-router"
# iptables rules (restore at boot ensure iptables-restore service exists)
mkdir -p "$IPTABLES_SAVE"
install -m 644 "$ETC/iptables/rules.v4" "$IPTABLES_SAVE/rules.v4"
echo "Installed $IPTABLES_SAVE/rules.v4"
if [ -d /etc/init.d ] && [ ! -f /etc/init.d/iptables-restore ]; then
echo "Tip: enable iptables restore at boot: rc-update add iptables-restore"
fi
# Log file
touch "$LOG_DIR/5g-router.log" 2>/dev/null && chmod 644 "$LOG_DIR/5g-router.log" || true
touch "$LOG_DIR/speedtest-5g.log" 2>/dev/null && chmod 644 "$LOG_DIR/speedtest-5g.log" || true
# Enable 5g-router at boot
if command -v rc-update >/dev/null 2>&1; then
rc-update add 5g-router default 2>/dev/null || true
echo "Added 5g-router to default runlevel"
fi
# Web GUI (optional)
WEBGUI_SRC="$ROOT/web"
WEBGUI_DEST="/usr/local/share/5g-webgui"
if [ -d "$WEBGUI_SRC" ] && [ -f "$WEBGUI_SRC/app.py" ]; then
mkdir -p "$WEBGUI_DEST"
cp -r "$WEBGUI_SRC"/* "$WEBGUI_DEST/"
chmod 755 "$WEBGUI_DEST/run.sh" 2>/dev/null || true
install -m 755 "$ETC/init.d/5g-webgui" "$INITD/5g-webgui"
echo "Installed Web GUI at $WEBGUI_DEST. Enable with: rc-update add 5g-webgui default"
echo " Then: apk add python3 py3-flask && service 5g-webgui start"
echo " Login at http://<device-ip>:5000 (default: admin/admin, support/support change passwords)"
else
echo "Web GUI source not found; skipping."
fi
echo ""
echo "Done. Edit $CONFIG if needed, then: service 5g-router start"
echo "Or run once: $BIN/connect-5g.sh"
echo "Status: $BIN/status-5g.sh"

105
scripts/modem-status-at.sh Normal file
View File

@@ -0,0 +1,105 @@
#!/bin/sh
# Alpine 5G Router modem status via AT commands (AT_COMMANDS_REFERENCE.md)
# Outputs JSON. Used by Web GUI /api/status.
# Rev: 2 (see REVISION in repo root)
CONFIG="/etc/5g-router.conf"
AT_PORT="${AT_PORT:-/dev/ttyUSB1}"
[ -f "$CONFIG" ] && . "$CONFIG"
get_at() {
_cmd="$1"
_wait="${2:-1}"
timeout $((_wait + 2)) sh -c "
cat $AT_PORT 2>/dev/null &
_p=\$!
sleep 0.3
echo -e '${_cmd}\r' > $AT_PORT
sleep $_wait
kill \$_p 2>/dev/null
" 2>/dev/null
}
json_str() { printf '"%s"' "$(echo "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\r//g' | tr '\n' ' ')"; }
# Probe port: send AT, return 0 if we see OK
try_at_port() {
_port="$1"
[ ! -c "$_port" ] && return 1
_out=$(timeout 4 sh -c "
cat $_port 2>/dev/null &
_p=\$!
sleep 0.3
echo -e 'AT\r' > $_port
sleep 1
kill \$_p 2>/dev/null
" 2>/dev/null)
echo "$_out" | grep -q 'OK' && return 0 || return 1
}
# Use configured port if it exists and is a char device; else try common Fibocom AT ports
if [ ! -c "$AT_PORT" ]; then
for p in /dev/ttyUSB0 /dev/ttyUSB1 /dev/ttyUSB2; do
if try_at_port "$p"; then
AT_PORT="$p"
break
fi
done
fi
[ ! -c "$AT_PORT" ] && echo '{}' && exit 0
# Disable command echo so we get only response lines (ATE0); avoids parsing "AT+CGMI" as manufacturer
get_at "ATE0" 1 >/dev/null 2>&1
sleep 0.2
# Query AT commands (reference: AT_COMMANDS_REFERENCE.md)
r_cgmi=$(get_at "AT+CGMI" 1)
r_cgmm=$(get_at "AT+CGMM" 1)
r_cgmr=$(get_at "AT+CGMR" 1)
r_cgsn=$(get_at "AT+CGSN" 1)
r_csq=$(get_at "AT+CSQ" 1)
r_creg=$(get_at "AT+CREG?" 1)
r_cereg=$(get_at "AT+CEREG?" 1)
r_usbmode=$(get_at "AT+GTUSBMODE?" 1)
r_ccid=$(get_at "AT+CCID" 1)
r_cops=$(get_at "AT+COPS?" 1)
r_cgpaddr=$(get_at "AT+CGPADDR=1" 1)
# Parse (skip echoed command lines: lines starting with AT or +)
manufacturer=$(echo "$r_cgmi" | grep -vE '^AT|^\+' | grep -oE '^[A-Za-z0-9 ].*' | head -1 | sed 's/\r$//; s/^ *//; s/ *$//')
model=$(echo "$r_cgmm" | grep -vE '^AT|^\+' | grep -oE '^[A-Za-z0-9\-].*' | head -1 | sed 's/\r$//; s/^ *//; s/ *$//')
revision=$(echo "$r_cgmr" | grep -vE '^(AT|OK|\+)' | head -1 | sed 's/\r$//; s/^ *//; s/ *$//')
imei=$(echo "$r_cgsn" | grep -oE '[0-9]{15}' | head -1)
csq=$(echo "$r_csq" | grep "+CSQ:" | grep -oE '[0-9]+' | head -1)
creg=$(echo "$r_creg" | grep "+CREG:" | head -1)
cereg=$(echo "$r_cereg" | grep "+CEREG:" | head -1)
usb_mode=$(echo "$r_usbmode" | grep "+GTUSBMODE:" | grep -oE '[0-9]+' | head -1)
iccid=$(echo "$r_ccid" | grep "+CCID:" | sed 's/+CCID:[[:space:]]*//; s/\r$//; s/"//g' | head -1)
cops=$(echo "$r_cops" | grep "+COPS:" | sed 's/+COPS:[[:space:]]*//; s/\r$//' | head -1)
modem_ip=$(echo "$r_cgpaddr" | grep "+CGPADDR:" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1)
# Registration: CREG 0,1 = registered home, 0,5 = registered roaming; CEREG same
creg_status=""
case "$creg" in *",1"*) creg_status="registered_home";; *",5"*) creg_status="registered_roaming";; *",2"*) creg_status="searching";; *",0"*) creg_status="not_registered";; *) creg_status="unknown";; esac
cereg_status=""
case "$cereg" in *",1"*) cereg_status="registered_home";; *",5"*) cereg_status="registered_roaming";; *",2"*) cereg_status="searching";; *",0"*) cereg_status="not_registered";; *) cereg_status="unknown";; esac
# Operator name from +COPS: 0,0,"Vodafone",7
operator_name=$(echo "$cops" | sed -n 's/.*"\([^"]*\)".*/\1/p')
# Build JSON (no jq)
printf '{\n'
printf ' "at_port": '; json_str "$AT_PORT"; printf ',\n'
printf ' "manufacturer": '; json_str "${manufacturer:-}"; printf ',\n'
printf ' "model": '; json_str "${model:-}"; printf ',\n'
printf ' "revision": '; json_str "${revision:-}"; printf ',\n'
printf ' "imei": '; json_str "${imei:-}"; printf ',\n'
printf ' "signal_csq": %s,\n' "${csq:-null}"
printf ' "creg_status": '; json_str "$creg_status"; printf ',\n'
printf ' "cereg_status": '; json_str "$cereg_status"; printf ',\n'
printf ' "usb_mode": '; json_str "${usb_mode:-}"; printf ',\n'
printf ' "iccid": '; json_str "${iccid:-}"; printf ',\n'
printf ' "operator": '; json_str "${operator_name:-}"; printf ',\n'
printf ' "modem_ip": '; json_str "${modem_ip:-}"; printf '\n'
printf '}\n'

21
scripts/rotate-5g-log.sh Normal file
View File

@@ -0,0 +1,21 @@
#!/bin/sh
# Rotate /var/log/5g-router.log (keep last 5 copies, max 1MB each)
# Run from cron daily: 0 3 * * * /usr/local/bin/rotate-5g-log.sh
# Alpine does not ship logrotate by default; this is a minimal alternative.
# Rev: 1 (see REVISION in repo root)
LOG="/var/log/5g-router.log"
MAXSIZE=$((1024 * 1024)) # 1MB
KEEP=5
[ ! -f "$LOG" ] && exit 0
size=$(stat -c %s "$LOG" 2>/dev/null) || size=$(wc -c < "$LOG" 2>/dev/null) || exit 0
[ "$size" -lt "$MAXSIZE" ] && exit 0
for i in $(seq $((KEEP - 1)) -1 1); do
[ -f "${LOG}.$i" ] && mv "${LOG}.$i" "${LOG}.$((i+1))"
done
[ -f "${LOG}.1" ] && mv "${LOG}.1" "${LOG}.2"
mv "$LOG" "${LOG}.1"
touch "$LOG"
chmod 644 "$LOG" 2>/dev/null || true

14
scripts/speedtest-5g.sh Normal file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
# Optional: run speedtest and append to log (for cron)
# Requires: apk add speedtest-cli
# Cron example: 0 */6 * * * /usr/local/bin/speedtest-5g.sh
# Rev: 1 (see REVISION in repo root)
LOG="/var/log/speedtest-5g.log"
if command -v speedtest-cli >/dev/null 2>&1; then
echo "=== $(date -Iseconds) ===" >> "$LOG"
speedtest-cli --simple >> "$LOG" 2>&1
echo "" >> "$LOG"
else
echo "$(date -Iseconds) speedtest-cli not installed" >> "$LOG"
fi

26
scripts/ssh-diag-modem.sh Executable file
View File

@@ -0,0 +1,26 @@
#!/bin/sh
# Alpine 5G Router run modem diagnostics on device via SSH
# Usage: ./scripts/ssh-diag-modem.sh [user@host]
# Or: TARGET=root@192.168.1.1 ./scripts/ssh-diag-modem.sh
TARGET="${1:-${TARGET:-root@10.130.60.121}}"
SSH_OPTS="${SSH_OPTS:--o ConnectTimeout=10}"
echo "Running modem diagnostics on $TARGET"
echo ""
ssh $SSH_OPTS "$TARGET" "
echo '========== diag-modem-up.sh =========='
if [ -x /usr/local/bin/diag-modem-up.sh ]; then
/usr/local/bin/diag-modem-up.sh
else
echo 'Not installed. Deploy first: ./scripts/deploy.sh $TARGET'
echo 'Then re-run this script.'
exit 1
fi
echo ''
echo '========== diag-at-port.sh =========='
if [ -x /usr/local/bin/diag-at-port.sh ]; then
/usr/local/bin/diag-at-port.sh
fi
"

44
scripts/status-5g.sh Normal file
View File

@@ -0,0 +1,44 @@
#!/bin/sh
# Alpine 5G Router status (modem, interface, default route, last speedtest)
# Usage: status-5g.sh [--json] (optional machine-readable output)
# Rev: 1 (see REVISION in repo root)
CONFIG="/etc/5g-router.conf"
WAN_IF="${WAN_IF:-eth1}"
LAN_IF="${LAN_IF:-eth0.100}"
AT_PORT="${AT_PORT:-/dev/ttyUSB1}"
[ -f "$CONFIG" ] && . "$CONFIG"
SPEEDTEST_LOG="/var/log/speedtest-5g.log"
MODEM_USB=$(lsusb 2>/dev/null | grep -i fibocom | head -1)
DEFAULT_ROUTE=$(ip route show default 2>/dev/null | head -1)
WAN_IP=$(ip -4 addr show "$WAN_IF" 2>/dev/null | grep -oE 'inet [0-9.]+' | awk '{print $2}')
WAN_STATE=$(ip link show "$WAN_IF" 2>/dev/null | grep -oE 'state [A-Z]+' | awk '{print $2}')
LAST_SPEEDTEST=""
[ -f "$SPEEDTEST_LOG" ] && LAST_SPEEDTEST=$(tail -5 "$SPEEDTEST_LOG" 2>/dev/null)
if [ "$1" = "--json" ]; then
echo "{"
echo " \"modem_usb\": \"${MODEM_USB:-none}\","
echo " \"wan_interface\": \"$WAN_IF\","
echo " \"wan_state\": \"${WAN_STATE:-unknown}\","
echo " \"wan_ip\": \"${WAN_IP:-}\","
echo " \"default_route\": \"${DEFAULT_ROUTE:-none}\","
echo " \"at_port\": \"$AT_PORT\","
echo " \"at_available\": \"$([ -c \"$AT_PORT\" ] && echo yes || echo no)\""
echo "}"
exit 0
fi
echo "=== 5G Router Status ==="
echo "Modem: ${MODEM_USB:-not detected}"
echo "AT port: $AT_PORT ($([ -c "$AT_PORT" ] && echo "available" || echo "not available"))"
echo "WAN: $WAN_IF state=$WAN_STATE ip=$WAN_IP"
echo "Default: $DEFAULT_ROUTE"
echo ""
echo "--- Last speedtest ---"
if [ -n "$LAST_SPEEDTEST" ]; then
echo "$LAST_SPEEDTEST"
else
echo "(none run speedtest-cli --simple and redirect to $SPEEDTEST_LOG)"
fi

145
scripts/troubleshoot-5g.sh Normal file
View File

@@ -0,0 +1,145 @@
#!/bin/sh
# Alpine 5G Router full troubleshoot: collect all logs and run diagnostics
# Run on device: /usr/local/bin/troubleshoot-5g.sh
# Or via SSH: ssh root@device /usr/local/bin/troubleshoot-5g.sh
# Rev: 1 (see REVISION in repo root)
CONFIG="/etc/5g-router.conf"
AT_PORT="${AT_PORT:-/dev/ttyUSB1}"
WAN_IF="${WAN_IF:-eth1}"
LOG_FILE="/var/log/5g-router.log"
[ -f "$CONFIG" ] && . "$CONFIG"
echo "=============================================="
echo " Alpine 5G Router Full Troubleshoot"
echo " $(date -Iseconds)"
echo "=============================================="
echo ""
# --- 1) Kernel / USB / tty (recent) ---
echo "--- 1) Kernel messages (dmesg, last 40 lines) ---"
dmesg 2>/dev/null | tail -40 || echo "(dmesg not available)"
echo ""
# --- 2) USB devices ---
echo "--- 2) USB devices (lsusb) ---"
lsusb 2>/dev/null || echo "(lsusb not available)"
if lsusb 2>/dev/null | grep -q "0e8d:7127"; then
echo " -> WARN: Modem in Mode 41 (7127). AT port may not work. Need Mode 40 (7126)."
elif lsusb 2>/dev/null | grep -q "0e8d:7126"; then
echo " -> Modem in Mode 40 (RNDIS) OK for AT on ttyUSB1"
fi
echo ""
# --- 3) Serial / AT port nodes ---
echo "--- 3) Serial devices (/dev/ttyUSB[0-9]*, /dev/ttyACM[0-9]*) ---"
if [ -e /dev/ttyUSB ] && [ -f /dev/ttyUSB ] && [ ! -c /dev/ttyUSB ]; then
echo " Stray file /dev/ttyUSB (no number) remove with: rm /dev/ttyUSB"
fi
for d in /dev/ttyUSB0 /dev/ttyUSB1 /dev/ttyUSB2 /dev/ttyUSB3 /dev/ttyUSB4 /dev/ttyUSB5 /dev/ttyACM0 /dev/ttyACM1; do
[ -e "$d" ] || continue
ls -la "$d" 2>/dev/null
if [ -f "$d" ] && [ ! -c "$d" ]; then
_n="${d#/dev/ttyUSB}"; _n="${_n#/dev/ttyACM}"
echo " -> BAD: $d is a regular file. Fix: rm $d && mknod $d c 188 $_n && chmod 660 $d && chown root:dialout $d"
fi
done
if ! ls /dev/ttyUSB[0-9]* /dev/ttyACM[0-9]* 2>/dev/null | grep -q .; then
echo " No ttyUSB/ttyACM devices. Modem may not be bound or not in Mode 40."
fi
echo ""
# --- 4) Config ---
echo "--- 4) Config ($CONFIG) ---"
if [ -f "$CONFIG" ]; then
grep -E '^[A-Za-z_]+=' "$CONFIG" 2>/dev/null | sed 's/^/ /' || true
else
echo " File not found (using defaults: AT_PORT=$AT_PORT, WAN_IF=$WAN_IF)"
fi
echo ""
# --- 5) AT port test (with longer wait like connect-5g.sh) ---
echo "--- 5) AT command test on $AT_PORT (wait 3s) ---"
if [ -c "$AT_PORT" ]; then
out=$(timeout 8 sh -c "
cat $AT_PORT 2>/dev/null &
_p=\$!
sleep 0.5
echo -e 'AT\r' > $AT_PORT 2>/dev/null
sleep 3
kill \$_p 2>/dev/null
" 2>&1)
if echo "$out" | grep -q 'OK'; then
echo " AT response: OK (modem responding)"
else
echo " AT response: no OK (modem not responding or wrong port)"
echo " Raw output:"
echo "$out" | head -10 | sed 's/^/ /'
fi
else
echo " $AT_PORT not available (missing or not a character device)"
fi
echo ""
# --- 6) AT probe on all ttyUSB (which port responds) ---
echo "--- 6) AT probe on each ttyUSB port ---"
for port in /dev/ttyUSB0 /dev/ttyUSB1 /dev/ttyUSB2 /dev/ttyUSB3; do
[ -c "$port" ] || continue
out=$(timeout 5 sh -c "cat $port 2>/dev/null & _p=\$!; sleep 0.3; echo -e 'AT\r' > $port; sleep 2; kill \$_p 2>/dev/null" 2>/dev/null)
if echo "$out" | grep -q 'OK'; then
echo " $port: OK (use this as AT_PORT if different from config)"
else
echo " $port: no OK"
fi
done
echo ""
# --- 7) 5g-router service ---
echo "--- 7) 5g-router service ---"
if command -v rc-service >/dev/null 2>&1; then
rc-service 5g-router status 2>&1 | sed 's/^/ /'
else
echo " OpenRC not found"
fi
echo ""
# --- 8) WAN interface and routing ---
echo "--- 8) WAN interface ($WAN_IF) and default route ---"
if ip link show "$WAN_IF" >/dev/null 2>&1; then
ip link show "$WAN_IF" 2>/dev/null | sed 's/^/ /'
ip -4 addr show "$WAN_IF" 2>/dev/null | sed 's/^/ /'
else
echo " Interface $WAN_IF does not exist"
fi
ip route show default 2>/dev/null | sed 's/^/ /'
echo ""
# --- 9) Full 5g-router log ---
echo "--- 9) Full 5g-router log (last 60 lines of $LOG_FILE) ---"
if [ -f "$LOG_FILE" ]; then
tail -60 "$LOG_FILE" 2>/dev/null | sed 's/^/ /'
else
echo " Log file not found"
fi
echo ""
# --- 10) Optional: modem status script ---
if [ -x "/usr/local/bin/modem-status-at.sh" ]; then
echo "--- 10) modem-status-at.sh (registration, signal) ---"
/usr/local/bin/modem-status-at.sh 2>&1 | head -30 | sed 's/^/ /'
else
echo "--- 10) modem-status-at.sh ---"
echo " Not installed"
fi
echo ""
echo "=============================================="
echo " End troubleshoot copy this output to share"
echo "=============================================="
echo ""
echo "Quick fixes to try:"
echo " - ttyUSB is regular file: rm $AT_PORT && mknod $AT_PORT c 188 1 && chmod 660 $AT_PORT && chown root:dialout $AT_PORT"
echo " - Modem in Mode 41 (7127): power-cycle modem or reboot; need Mode 40 (7126) for AT"
echo " - AT not OK: set AT_PORT in $CONFIG to the port that showed OK above (e.g. /dev/ttyUSB0)"
echo " - Restart connection: service 5g-router restart or /usr/local/bin/connect-5g.sh"