#!/bin/bash # Fibocom FM350-GL 5G Modem Configuration Script # For Alpine Linux. Supports config file /etc/5g-router.conf # Usage: ./configure_fm350_5g.sh # Rev: 1 (see REVISION in repo root) set -e # Defaults (overridden by /etc/5g-router.conf if present) AT_PORT="${AT_PORT:-/dev/ttyUSB1}" APN="${APN:-internet}" WAN_IF="${WAN_IF:-eth1}" LAN_IF="${LAN_IF:-eth0.100}" LOG_SIGNAL="${LOG_SIGNAL:-no}" WEAK_SIGNAL_CSQ="${WEAK_SIGNAL_CSQ:-0}" [ -f /etc/5g-router.conf ] && . /etc/5g-router.conf LOG_FILE="${LOG_FILE:-/var/log/5g-router.log}" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } send_at_cmd() { local cmd="$1" local wait="${2:-2}" [ -c "$AT_PORT" ] || return 1 cat $AT_PORT 2>/dev/null & local CAT_PID=$! sleep 0.3 [ -c "$AT_PORT" ] || { kill $CAT_PID 2>/dev/null; return 1; } echo -e "${cmd}\r" > $AT_PORT sleep $wait kill $CAT_PID 2>/dev/null || true } get_at_response() { local cmd="$1" local wait="${2:-2}" [ -c "$AT_PORT" ] || return 1 timeout $((wait + 3)) sh -c " [ -c \"$AT_PORT\" ] || exit 1 cat $AT_PORT 2>/dev/null & CAT_PID=\$! sleep 0.3 echo -e '${cmd}\r' > $AT_PORT sleep $wait kill \$CAT_PID 2>/dev/null " 2>&1 } check_modem() { log_info "Checking for FM350-GL modem..." if ! lsusb | grep -qi "fibocom"; then log_error "Fibocom modem not detected via USB" exit 1 fi local usb_id=$(lsusb | grep -i fibocom | grep -oE '0e8d:[0-9a-f]+') log_info "Modem detected: $usb_id" if [ "$usb_id" = "0e8d:7127" ]; then log_warn "Modem is in Mode 41 (7127). AT commands may not work." log_warn "Attempting to switch to Mode 40 (modem will reset)..." get_at_response "AT+GTUSBMODE=40" 2 >/dev/null || true get_at_response "AT+CFUN=1,1" 5 >/dev/null || true log_info "Wait ~30s for modem to reappear, then re-run this script." exit 1 fi if [ ! -c "$AT_PORT" ]; then log_error "AT port $AT_PORT not available" exit 1 fi log_info "AT port available: $AT_PORT" } test_at_commands() { log_info "Testing AT command communication..." local response=$(get_at_response "AT" 1) if echo "$response" | grep -q "OK"; then log_info "AT communication working" return 0 else log_error "AT commands not responding" return 1 fi } check_signal() { log_info "Checking signal strength..." local response=$(get_at_response "AT+CSQ" 1) local csq=$(echo "$response" | grep "+CSQ:" | grep -oE '[0-9]+' | head -1) if [ -n "$csq" ]; then if [ "$csq" -lt 10 ]; then log_warn "Signal strength: $csq (weak)" elif [ "$csq" -lt 20 ]; then log_info "Signal strength: $csq (moderate)" else log_info "Signal strength: $csq (good)" fi [ "$LOG_SIGNAL" = "yes" ] && echo "$(date -Iseconds) CSQ=$csq" >> "$LOG_FILE" 2>/dev/null || true if [ -n "$WEAK_SIGNAL_CSQ" ] && [ "$WEAK_SIGNAL_CSQ" -gt 0 ] && [ "$csq" -lt "$WEAK_SIGNAL_CSQ" ]; then log_warn "Signal below threshold ($WEAK_SIGNAL_CSQ): $csq" [ -w "$LOG_FILE" ] && echo "$(date -Iseconds) WARN weak signal CSQ=$csq" >> "$LOG_FILE" || true fi else log_warn "Could not read signal strength" fi } configure_apn() { log_info "Configuring APN: $APN" local response=$(get_at_response "AT+CGDCONT=1,\"IP\",\"$APN\"" 2) if echo "$response" | grep -q "OK"; then log_info "APN configured successfully" else log_warn "APN configuration may have failed" fi } activate_pdp() { log_info "Activating PDP context..." local response=$(get_at_response "AT+CGACT=1,1" 3) if echo "$response" | grep -qE "(OK|CGEV)"; then log_info "PDP context activated" else log_warn "PDP activation response: $response" fi } get_modem_ip() { log_info "Getting modem IP address..." local response=$(get_at_response "AT+CGPADDR=1" 2) local ip=$(echo "$response" | grep "+CGPADDR:" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -1) if [ -n "$ip" ]; then log_info "Modem IP: $ip" echo "$ip" else log_error "Could not get modem IP" return 1 fi } configure_interface() { local ip="$1" log_info "Configuring network interface $WAN_IF..." 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_info "Interface configured with IP: $ip" } setup_nat() { log_info "Setting up NAT for LAN ($LAN_IF)..." # Enable IP forwarding echo 1 > /proc/sys/net/ipv4/ip_forward # Add NAT rules (check if they exist first) 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_info "NAT configured" } test_connectivity() { log_info "Testing internet connectivity..." if ping -c 3 8.8.8.8 >/dev/null 2>&1; then log_info "Ping to 8.8.8.8: SUCCESS" else log_error "Ping to 8.8.8.8: FAILED" return 1 fi if ping -c 2 google.com >/dev/null 2>&1; then log_info "DNS resolution: SUCCESS" else log_warn "DNS resolution failed - check /etc/resolv.conf" fi } show_status() { echo "" echo "=========================================" echo " 5G Modem Configuration Summary " echo "=========================================" echo "" echo "Modem: $(lsusb | grep -i fibocom | cut -d' ' -f6-)" echo "AT Port: $AT_PORT" echo "APN: $APN" echo "" echo "Interface: $WAN_IF" ip addr show $WAN_IF 2>/dev/null | grep -E "(state|inet )" | sed 's/^/ /' echo "" echo "Routing:" ip route show | grep -E "(default|$WAN_IF)" | sed 's/^/ /' echo "" echo "=========================================" } # Main execution main() { log_info "Starting FM350-GL 5G modem configuration..." echo "" check_modem test_at_commands || exit 1 check_signal configure_apn activate_pdp local modem_ip=$(get_modem_ip) if [ -z "$modem_ip" ]; then log_error "Failed to get modem IP. Exiting." exit 1 fi configure_interface "$modem_ip" setup_nat test_connectivity show_status log_info "5G modem configuration complete!" } main "$@"