- 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
169 lines
5.9 KiB
Bash
169 lines
5.9 KiB
Bash
#!/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 3–5s 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
|