My home internet connection is delivered via cellular modem connected by USB to an ASUS wireless router. Reliability of this connection is poor. With a number of IoT devices, small home servers, and other active devices on the LAN, the internet connection via USB modem would often drop out, sometimes once per day, or at least a few times per week. Under heavier load, sometimes during overnight file synchronizations to/from a cloud source, the problem seemed to increase.
It’s likely that a superior router or USB modem would eliminate this issue—I don’t know for sure what the actual cause of the problem—but when this occurs, a router reboot resolves the issue and restores connectivity.
Initially, I would manually reboot the router when an outage occurred. Sometimes, though, I would not be able to access a home server for testing during the day while at work, because the connection had been dropped. This was a further concern when I installed a Sense energy monitor, which requires constant connectivity to build profiles on electrical devices running. During an extended outage, all data would be lost. A frustrating setback.
The router’s built-in reboot scheduler was a second step towards a solution. This helped a lot. It was much more rare that I experienced outages. But they would still occur at times, and depending on timing, it could be hours before an automatic resolution, which would exhaust the Sense monitor’s internal buffer, leading, again, to data loss.
To minimize downtime, I wanted to actively monitor the internet connection, and reboot the router whenever an offline state was detected. This also provided the opportunity for a dive into the possibilities of programming the ASUS router with its default firmware. And fortunately, this was possible to do entirely on the router itself.
1. Enable SSH
The first step was to enable SSH connectivity on the router from the Administration > System section of the router’s control pages.
2. Log in to the router’s SSH server
The same user account with admin access to the router’s web configuration pages will have SSH access. Optionally, disable password login and configure keys. All remaining steps are executed via SSH.
3. Write a script to check internet connection and reboot
An extremely simple script, the operation of which is to ping google.com 5 times. If packets are received in reply, ping exits with a code of 0 and we show a simple message and exit. Else, if no packets are received in reply, ping will exit with a code of 1 and we proceed to log a message to a file and reboot the router.
#!/bin/sh ping -c5 google.com if [ $? -eq 0 ]; then date +"%Y-%m-%d %r - UP" else date +"%Y-%m-%d %r - DOWN/REBOOT" >> /jffs/scripts/check.log reboot fi
An important note is that I am logging the message to a new file in /jffs/. On these routers, the /jffs/ location refers to a small, persistent, writable flash partition (Journaling Flash File System) mounted on the linux system and available for storing small scripts and such. This location is writable, survives a reboot, and is mounted earlier in the boot process than any USB drives.
In addition to logging to this location, I also save this script in the same location, at /jffs/scripts/internetcheck.sh
After making the script executable, a simple test can confirm its behavior:
chmod +x /jffs/scripts/internetcheck.sh /jffs/scripts/internetcheck.sh PING google.com (18.104.22.168): 56 data bytes 64 bytes from 22.214.171.124: seq=0 ttl=49 time=111.127 ms 64 bytes from 126.96.36.199: seq=1 ttl=49 time=45.386 ms 64 bytes from 188.8.131.52: seq=2 ttl=49 time=41.460 ms 64 bytes from 184.108.40.206: seq=3 ttl=49 time=143.760 ms 64 bytes from 220.127.116.11: seq=4 ttl=49 time=65.745 ms --- google.com ping statistics --- 5 packets transmitted, 5 packets received, 0% packet loss round-trip min/avg/max = 41.460/81.495/143.760 ms 2018-07-30 09:19:56 PM - UP
A false DNS name can be tested to confirm the failure mode and reboot. This was successful. So now, how to schedule it?
4. Scheduling the script to execute
These routers do include crond for task scheduling, however as with much of the system, /var/spool/cron/crontabs/ is mounted as a location in RAM, and does not survive a reboot. So it is necessary to add the scheduled task again every time the router reboots.
To do this, we need another script, which I create at /jffs/scripts/init-start
#!/bin/sh cru a internetcheck "*/30 * * * * /jffs/scripts/internetcheck.sh"
We can either use the script to copy an existing cron file from jffs to the crontabs location, or use the built-in cru utility to add the cron, as I do above.
The final question—how do we run this script on boot, to set the cron? There is no direct way to hook into a bootup script. But there is an option to run a script every time a USB drive is mounted, which happens on boot.
To do this, we configure the parameter in nvram:
nvram set script_usbmount="/jffs/scripts/init-start" nvram commit
Now, on every boot, the cron job will be added, begin executing, check the internet connection every 30 minutes, and, if necessary, reboot the router and start the whole process over again. After a few days, this is working smoothly and is effective as a hack to patch over this problem of suboptimal hardware interaction. Besides the question of long-term reasonableness, the main thing I’d like to improve is logging for more visibility.