Files
Alpine_5G/scripts/connect-5g.sh
nearxos 9dc35a57a2 Enhance 5G modem management with integrated web GUI and connection control
- Introduced a web GUI for managing 5G connections, replacing the standalone 5g-router service.
- Updated scripts to ensure exclusive access to the AT port, preventing conflicts.
- Improved troubleshooting documentation in 5G_MODEM_TROUBLESHOOTING.md, adding checks for processes using the AT port.
- Enhanced connection management in the web app, including auto-connect and detailed status APIs.
- Updated installation scripts to reflect changes in service management and dependencies.
2026-02-02 10:34:25 +02:00

210 lines
7.1 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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" || true
# Lock file for exclusive AT port access (shared with modem-status-at.sh)
AT_LOCK="/var/lock/5g-at.lock"
exec 9>"$AT_LOCK"
flock -n 9 || { echo "Another process is using the AT port, waiting..." >&2; flock 9; }
# Log to file; also to stdout only when running in a terminal (avoids duplicate lines when service redirects stdout to log)
log() {
_m="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
echo "$_m" >> "$LOG_FILE" 2>/dev/null || true
[ -t 1 ] && echo "$_m" || true
}
# Fix broken /dev/ttyUSB* node (if it is a regular file instead of char device - e.g. after modem reconnect)
# Also remove stray /dev/ttyUSB (no digit) which can appear and steal the name from real devices
fix_ttyusb_if_needed() {
# Remove stray /dev/ttyUSB (no number) if it's a regular file
if [ -e /dev/ttyUSB ] && [ -f /dev/ttyUSB ] && [ ! -c /dev/ttyUSB ]; then
rm -f /dev/ttyUSB
fi
case "$AT_PORT" in
/dev/ttyUSB[0-9]*) ;;
*) return 0 ;;
esac
[ ! -e "$AT_PORT" ] && return 0 || true
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
}
# Never write to AT_PORT unless it is a character device; otherwise a redirect would create a regular file and break the port
get_at_response() {
_cmd="$1"
_wait="${2:-2}"
timeout $((_wait + 4)) sh -c "
[ -c \"$AT_PORT\" ] || exit 1
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 515s to respond after port appears at boot; retry initial AT up to 3 times
_at_ok=0
for _try in 1 2 3; do
if get_at_response "AT" 5 | grep -q "OK"; then
_at_ok=1
break
fi
if [ $_try -lt 3 ]; then
log "AT not OK, retry $_try/3 in 5s..."
sleep 5
fi
done
if [ $_at_ok -eq 0 ]; then
log "AT not OK (try longer wait or different AT port)"
return 1
fi
# 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" || true
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)
if [ -n "$_ip" ]; then break; fi
if [ $_retry -lt 5 ]; then
log "No IP yet, retry $_retry/5 in 3s..."
sleep 3
fi
done
if [ -z "$_ip" ]; then
log "Could not get modem IP (check signal/registration: AT+CSQ, AT+CEREG?)"
return 1
fi
# 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 || true
_allips="${_allips} ${_a}"
done
for _a in $_allips; do
[ -z "$_a" ] && continue || true
_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 || true
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 || true
# 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