Cell phone hotspot to OpenWRT One as backup to main ISP

I first tried Travelmate, but it caused CPU spikes. I then tried MultiWAN Manager, but it was slow, clunky and kept misconfiguring the connections.

So I scripted my own.

This script monitors for a certain SSID on radio0 (2.4 GHz). That SSID comes from a cell phone hotspot. If your main ISP goes down, you can feed data to/from your LAN through your cell phone’s hotspot.

The script monitors periodically (using the main 2.4GHz WiFi) for the TARGET_SSID. When the script sees the TARGET_SSID, it enables the wireless network which is set up to receive the cell phone hotspot WiFi signal; and when it no longer sees that TARGET_SSID, it disables that wireless network and its associated interface, and brings radio0 back up so the 2.4 GHz signal remains stable.

This is due to a bug that causes radio0 (2.4GHz) to go down and stay down after you stop the hotspot signal, usually requiring a manual restart of radio0. There’s also a bug wherein the wireless network set up to receive the hotspot signal loses the interface it’s assigned to when the hotspot WiFi is disabled… this script assigns that interface just before bringing up the wireless network set up to receive the hotspot signal.

DO NOT use radio1 (5GHz) for receiving a hotspot signal. It just doesn’t work. radio1 goes down when it tries, requiring a manual restart of radio1. Set your cellphone’s hotspot to only transmit on 2.4GHz.

Go to Network >> Interfaces. Click the “Add new interface” button.

Add new interface…: Name: Choose a name. I chose trm_wwan.

Add new interface…: Protocol: DHCP client

Add new interface…: Device: phy0-sta0

Click the “Create Interface” button.

Back in the list of interfaces, find the interface you just created, and click its “Edit” button.

Set up the new interface thusly:

General Settings: Protocol: DHCP client

General Settings: Device: phy0-sta0

General Settings: Disable this interface: unchecked

General Settings: Bring up on boot: unchecked

General Settings: Hostname to send when requesting DHCP: Send the hostname of this device

Advanced Settings: Force link: unchecked

Advanced Settings: Use broadcast flag: unchecked

Advanced Settings: Preferred client ID: Automatic

Advanced Settings: Client ID to send when requesting DHCP: blank

Advanced Settings: Vendor Class to send when requesting DHCP: blank

Advanced Settings: Use default gateway: checked

Advanced Settings: Use DNS servers advertised by peer: unchecked

Advanced Settings: Use custom DNS servers: 1.1.1.1; 1.0.0.1; 9.9.9.9; 149.112.112.112

Advanced Settings: DNS weight: 0

Advanced Settings: Use gateway metric: 100

Advanced Settings: Multi-Path TCP: Off

Advanced Settings: Override IPv4 routing table: blank

Advanced Settings: Override IPv6 routing table: blank

Advanced Settings: Delegate IPv6 prefixes: checked

Advanced Settings: IPv6 assignment length: disabled

Advanced Settings: IPv6 prefix filter: blank

Advanced Settings: IPv6 suffix: ::1

Advanced Settings: IPv6 preference: 0

Firewall Settings: Create/Assign firewall-zone: wan + wan6

DHCP Server: No DHCP Server configured for this interface.

Click the “Save” button.

Back at Network >> Interfaces, click the “Add new interface…” button again.

Add new interface…: Name: Choose a name. I chose trm_wwan6.

Add new interface…: Protocol: DHCPv6 client

Add new interface…: Device: Alias Interface: @{whatever you named the first interface}

Mine would be: Alias Interface: “@trm_wwan

Click the “Create Interface” button.

Back in the list of interfaces, find the interface you just created, and click its “Edit” button.

Set up the new interface thusly:

General Settings: Protocol: DHCPv6 client

General Settings: Device: @{whatever you named the first interface}

Mine would be: @trm_wwan

General Settings: Disable this interface: unchecked

General Settings: Bring up on boot: checked

General Settings: Request IPv6-address: try

General Settings: Request IPv6-prefix: Automatic

General Settings: Extend prefix: unchecked

General Settings: Do not send a Release when restarting: checked

Advanced Settings: Force link: unchecked

Advanced Settings: Preferred client ID: Automatic

Advanced Settings: Client ID to send when requesting DHCP: blank

Advanced Settings: Use default gateway: checked

Advanced Settings: Use DNS servers advertised by peer: checked

Advanced Settings: DNS weight: 0

Advanced Settings: Use gateway metric: 100

Advanced Settings: Multi-Path TCP: Off

Advanced Settings: Override IPv4 routing table: blank

Advanced Settings: Override IPv6 routing table: blank

Advanced Settings: IPv6 source routing: checked

Advanced Settings: Delegate IPv6 prefixes: checked

Advanced Settings: IPv6 assignment length: disabled

Advanced Settings: IPv6 prefix filter: blank

Advanced Settings: IPv6 suffix: ::1

Advanced Settings: IPv6 preference: 0

Firewall Settings: Create/Assign firewall-zone: wan + wan6

DHCP Server: No DHCP Server configured for this interface.

Click the “Save” button.

Go to Network >> Wireless. Click the “Add” button for radio0.

Set up the new wireless network thusly:

Device Configuration: General Setup: Mode: AX

Device Configuration: General Setup: Channel: auto

Device Configuration: General Setup: Width: 20 MHz

Device Configuration: General Setup: Allow 802.11b rates: unchecked

Device Configuration: General Setup: Maximum transmit power: driver default

Device Configuration: General Setup: Country Code: your country

Device Configuration: Advanced Settings: Coverage cell density: Disabled

Device Configuration: Advanced Settings: Distance Optimization: auto

Device Configuration: Advanced Settings: Fragmentation Threshold: off

Device Configuration: Advanced Settings: RTS/CTS Threshold: off

Device Configuration: Advanced Settings: Force 40MHz mode: unchecked

Device Configuration: Advanced Settings: Beacon Interval: 300

Device Configuration: Advanced Settings: Rx LDPC: checked

Device Configuration: Advanced Settings: Tx LDPC: checked

Interface Configuration: General Setup: Mode: Client

Interface Configuration: General Setup: ESSID: The hotspot’s SSID

Interface Configuration: General Setup: BSSID: blank

Interface Configuration: General Setup: Network: {the interface you just set up}

For mine, it would be trm-wwan.

Interface Configuration: Wireless Security: WPA2-PSK/WPA3-SAE Mixed Mode (strong security)

Interface Configuration: Wireless Security: Key: The hotspot SSID’s keyphrase

Interface Configuration: Wireless Security: 802.11w Management Frame Protection: Optional

Interface Configuration: Wireless Security: 802.11w maximum timeout: 1000

Interface Configuration: Wireless Security: 802.11w retry timeout: 201

Interface Configuration: Wireless Security: Operating Channel Validation: Disabled

Interface Configuration: Advanced Settings: Multi To Unicast: unchecked

Interface Configuration: Advanced Settings: Isolate Bridge Port: unchecked

Interface Configuration: Advanced Settings: Interface name: blank

Interface Configuration: Advanced Settings: MAC address: driver default (undefined)

Interface Configuration: Advanced Settings: Short Preamble: checked

Interface Configuration: Advanced Settings: DTIM Interval: 2

Interface Configuration: Advanced Settings: Time interval for rekeying GTK: 600

Interface Configuration: Advanced Settings: Disable Inactivity Polling: unchecked

Interface Configuration: Advanced Settings: Station inactivity limit: 300

Interface Configuration: Advanced Settings: Maximum allowed Listen Interval: 65535

Interface Configuration: Advanced Settings: Disassociate On Low Acknowledgement: checked

Click ‘Save’, then click ‘Save & Apply’ at the bottom of the page.

Start up your cell phone’s hotspot.

Go to Network >> Wireless, and for the new wireless network you just created, if its button says “Enable”, click to enable the wireless network, then click the “Save & Apply” button.

Wait for the router to connect to the cellphone hotspot. You’ll see the bars in the signal strength meter for that network turn blue, and numbers denoting the signal strength will display.

In Services >> Terminal, enter:

uci show wireless | grep .device=

It will display something like this:

wireless.default_radio0.device=‘radio0’

wireless.default_radio1.device=‘radio1’

wireless.wifinet2.device=‘radio0’

That last entry should be the wireless network you set up to connect to the hotspot.

To be absolutely sure, in Services >> Terminal, enter:

uci show wireless.wifinet2

It will display something like this:

wireless.wifinet2=wifi-iface

wireless.wifinet2.device=‘radio0’

wireless.wifinet2.mode=‘sta’

wireless.wifinet2.ssid=‘{SSID OF YOUR HOTSPOT}’

wireless.wifinet2.encryption=‘sae-mixed’

wireless.wifinet2.key=‘{PASSPHRASE FOR THAT SSID}’

wireless.wifinet2.ocv=‘0’

wireless.wifinet2.disabled=‘1’

wireless.wifinet2.network=‘trm_wwan’

If that is your hotspot, you’ll then change the code below to:

INTERFACE=“wifinet2”

NET_INTF=“trm_wwan”

Go to System >> Startup >> Local Startup.

Edit the code below to reflect your TARGET_SSID, your INTERFACE, your NET_INTF, your RADIO, the maximum_sleep you want (used to reduce CPU utilization) and whether you want logging (logging=1) or not (logging=0).

To see the logs in real-time, in Services >> Terminal, you’d enter:

logread -f | grep hotspot_monitor

At System >> Startup >> Local Startup, paste the code below the first two comment lines, but before the last (exit 0) line and click the “Save” button.

HOTSPOT MONITOR

TARGET_SSID=“Teracube”

INTERFACE=“wifinet2”

NET_INTF=“trm_wwan”

RADIO=“radio0”

current_sleep=20

missed_checks=0

minimum_sleep=20

maximum_sleep=90

logging=1

logger -t hotspot_monitor “Hotspot Monitor script started monitoring for SSID: $TARGET_SSID”

while true; do

# Check if the interface config is currently enabled in wireless settings
IS_ENABLED=$(uci get wireless.$INTERFACE.disabled 2>/dev/null)

# Check if the network interface layer is actually bound and running traffic

INTF_UP=$(ubus call network.interface.$NET_INTF status 2>/dev/null | jsonfilter -e ‘@.up’)

if [ “$IS_ENABLED” = “0” ]; then

   # -------------------------------------------------------------
   # IF CONNECTED: Trust UBUS network status. NEVER scan the airwaves!
   # -------------------------------------------------------------
   if [ "$INTF_UP" = "true" ]; then
       HOTSPOT_FOUND=1
   else
       HOTSPOT_FOUND=0
   fi

else

   # -------------------------------------------------------------
   # IF DISCONNECTED: Safe to scan because the client layer is disabled!
   # -------------------------------------------------------------
   if iwinfo phy0-ap0 scan 2>/dev/null | grep -q "ESSID: .$TARGET_SSID."; then
       HOTSPOT_FOUND=1
   else
       HOTSPOT_FOUND=0
   fi

fi

# -------------------------------------------------------------
# STATE ENGINE EXECUTION
# -------------------------------------------------------------

if [ “$HOTSPOT_FOUND” -eq 1 ]; then

   missed_checks=0
   
   if [ "$IS_ENABLED" = "1" ] || [ -z "$IS_ENABLED" ]; then
       if [ "$logging" = "1" ]; then
           logger -t hotspot_monitor "Hotspot $TARGET_SSID Found! Binding network to $NET_INTF and enabling client interface $INTERFACE."
       fi
       
       uci set wireless.$INTERFACE.disabled='0'
       uci set wireless.$INTERFACE.network="$NET_INTF"
       uci commit wireless
       wifi reload $RADIO
       
       current_sleep=$minimum_sleep
       sleep 25
   else
       if [ "$logging" = "1" ]; then
           logger -t hotspot_monitor "Hotspot stable. Scaling sleep timer. Sleeping for $current_sleep seconds."
       fi
       sleep "$current_sleep"
       
       current_sleep=$((current_sleep + 1))
       if [ "$current_sleep" -gt "$maximum_sleep" ]; then
           current_sleep=$maximum_sleep
       fi
   fi

else

   missed_checks=$((missed_checks + 1))
   if [ "$missed_checks" -gt 1000000 ]; then
       missed_checks=1000000
   fi
   
   if [ "$missed_checks" -ge 2 ]; then
       if [ "$IS_ENABLED" = "0" ]; then
           if [ "$logging" = "1" ]; then
               logger -t hotspot_monitor "Hotspot dropped! Missed checks: $missed_checks. Disabling interface $INTERFACE to keep radio0 stable."
           fi
           uci set wireless.$INTERFACE.disabled='1'
           uci commit wireless
           wifi reload $RADIO
           
           current_sleep=$minimum_sleep
           sleep 20
       else
           if [ "$logging" = "1" ]; then
               logger -t hotspot_monitor "Hotspot absent. Radio0 operational. Scaling sleep timer. Sleeping for $current_sleep seconds."
           fi
           sleep "$current_sleep"
           
           current_sleep=$((current_sleep + 1))
           if [ "$current_sleep" -gt "$maximum_sleep" ]; then
               current_sleep=$maximum_sleep
           fi
       fi
   else
       sleep 20
   fi

fi

done &

Then to start the script, either reboot the router, or issue the command:

sh /etc/rc.local

in Services >> Terminal.

Note that I’m using metrics… the cellphone hotspot has a metric of 100, whereas the main ISP has a metric of 1. So if both the main ISP and the cellphone hotspot are connected, data preferentially flows to/from the main ISP.