FON Failover Router

From DD-WRT Wiki

Jump to: navigation, search

With the inclusion of BT FON in new BT Home Hubs by default, there has been a proliferation of working access points; in many areas, users may well have a Fon or BTWiFI SSID within easy reach of their homes. The author already runs a FON hotspot on a Virgin cable connection, and now has a FON connection within reach, courtesy of his neighbours on BT.

So, this guide looks at using a second DD-WRT router to connect to the neighbour's wireless network, and make it available as a fallback option for when my connection goes down. My first approach to this was to configure a second DD-WRT router as a Repeater Bridge, which worked OK for repeating the wireless signal in order to connect a laptop. However, I wanted a solution that would (eventually) permit failover routing for the whole network, wired and wireless clients alike.

Prerequisites:

  • Primary LAN/WAN router running DD-WRT and a FON hotspot
  • Third-party hotspot (BTWiFi or FON) within wireless range
  • Secondary repeater to connect to the public access hotspot, also running DD-WRT

My setup:

  • 192.168.1.1 - my primary WAN router, connected via Virgin Media cable, D-link DIR-615 running DD-WRT
  • 192.168.1.2 - my own FON hotspot, a Fonera 2100 running DD-WRT
  • 192.168.1.3 - repeater for neighbour's FON hotspot, D-link DIR-615 running DD-WRT, connection via BT Internet

Repeater:

The secondary was set up in Repeater mode, connecting to "BTWiFi", no encryption, and it duly picks up a WAN IP of 10.66.243.x. The BT Openzone login and DNS servers use RFC1918 address space (192.168.x.x), and we need to be able to access these in order for our local connections to route to the FON/BT Openzone login page. The repeater obtains its nameserver settings from DHCP, but doesn't know how to route packets for 192.168.22.x and 192.168.23.x. So, I set up a static route on the repeater to 192.168.22.0/23, using the gateway and interface provided by our client connection, apcli0. This gets DNS resolution working on the repeater; we also then need to add a route to the same netblock on the primary router, using the repeater as gateway, so that our clients can reach the BT Openzone login and DNS servers.

Router:

Having added a new static route to 192.168.22.0/23 via our repeater at 192.168.1.3, verify that we can reach the BT Openzone DNS servers with a "nslookup www.btopenzone.com 192.168.22.22"; this should work both from the router itself and from a client connected behind the router. When it does, we need to tell our DHCP connected clients that they should try these nameservers, followed by OpenDNS. Use the "additional options to DNSmasq" box to configure the list of DHCP servers that we will push to the client. Of course, this risks making our DNS a lot less efficient any time that we don't have a route to the BT Openzone DNS servers, but I couldn't get it to play nice with forwarding, so the only sensible option was to push the DNS servers to the client through DHCP.

Switching the default route:

When we have the repeater successfully connected to the public hotspot, and to our LAN, routing configured for the BT Openzone DNS servers, and those servers pushed to the clients via DHCP, all that we need to do to switch to FON is to change the default route at the main router, then log in to FON/BT Openzone.

To switch from the cable connection to the FON connection:

route add default gw 192.168.1.3; 
route del default gw 80.195.81.1; 
ip route flush cache; 
kill -STOP `cat /var/run/udhcpc.pid`

To switch from the FON connection back to the cable connection:

route add default gw 80.195.81.1; 
route del default gw 192.168.1.3; 
ip route flush cache;
kill -CONT `cat /var/run/udhcpc.pid`

We suspend the DHCP client to the cable modem, because otherwise it will renew the DHCP lease periodically and mess up our default routing. If you're happy to log in and log out manually, then this is all you need in order to re-route all of your DHCP-configured clients on LAN or WLAN via the FON repeater.

Bundling it into a custom script:

It'd be useful to have a single command on the primary router to switch the network from my cable connection to FON and back again; at its most basic, this would look like:

#!/bin/ash
CABLE=80.195.81.1;
FON=192.168.1.3;
case $1 in
 fon)
  route add default gw $FON; 
  route del default gw $CABLE; 
  ip route flush cache; 
  kill -STOP `cat /var/run/udhcpc.pid`
 ;;
 cable)
  route add default gw $CABLE; 
  route del default gw $FON; 
  ip route flush cache; 
  kill -STOP `cat /var/run/udhcpc.pid`
 ;;
esac

This can be stored as a custom script via the DD-WRT gui and will end up on the filesystem as /tmp/custom.sh, so we can switch to FON with a "/tmp/custom.sh fon" or switch back to cable with a "/tmp/custom.sh cable".

Adding automated fail-over:

For proper automated fail-over, we need to detect whether our primary gateway is reachable and trigger a switch to FON if it is not. That should persist only until our primary gateway becomes reachable again, at which point we want to fail-back to default operation. We can achieve that by modifying the custom script like this:

#!/bin/ash
PATH=/bin:/usr/bin:/sbin:/usr/sbin:/jffs/sbin:/jffs/bin:/jffs/usr/sbin:/jffs/usr/bin:/mmc/sbin:/mmc/bin:/mmc/usr/sbin:/mmc/usr/bin:/opt/sbin:/opt/bin:/opt/usr/sbin:/opt/usr/bin
CABLE=80.195.81.1;
FON=192.168.1.3;
case $1 in
fon)
 /sbin/route add default gw $FON; 
 /sbin/route del default gw $CABLE; 
 /usr/sbin/ip route flush cache; 
 /bin/kill -STOP `cat /var/run/udhcpc.pid`
;;
cable)
 /sbin/route add default gw $CABLE; 
 /sbin/route del default gw $FON; 
 /usr/sbin/ip route flush cache; 
 /bin/kill -STOP `cat /var/run/udhcpc.pid`
;;
cron)
 RT=`/sbin/route -n | /bin/grep -o $CABLE`
 UP=`/bin/ping -c1 -w1 $CABLE 2>/dev/null | /bin/grep packets | /usr/bin/awk {'print $4'}`
 if [ -z "$RT" ] && [ $UP -eq 1 ]; then 
   /bin/echo "Switching from FON to CABLE";
   $0 cable;
 fi
 if [ ! -z "$RT" ] && [ $UP -eq 0 ]; then 
   /bin/echo "CABLE down, switching to FON";
   $0 fon;
 fi
 if [ ! -z "$RT" ] && [ $UP -eq 1 ]; then 
   /bin/echo "CABLE up, leaving CABLE untouched";
 fi
 if [ -z "$RT" ] && [ $UP -eq 0 ]; then 
   /bin/echo "CABLE down, leaving FON untouched";
 fi
;;
esac

which gives us a new command,./tmp/custom.sh cron. This evaluates whether we can find the upstream cable gateway in our routing table, and whether we can get a ping response from that gateway. If both are true, we're on cable, all is well, do nothing. If both are false, we're on FON and we can't reach the cable ISP, so do nothing. If we're routed via cable but we can't reach our nexthop, fail-over to FON. If we're routed via FON but the cable modem is available, failback to the default route. Detection of outages is extremely crude, relying on a single ping to the known next hop, and it will probably be necessary to include slightly more testing before switching from one route to another.

However, the failover is working inasmuch as with the script crontabbed to run every minute, I can pull the wire on the WAN connection and the primary router will detect that the WAN connection is down, and switch the routing over to FON. Login is still left up to the user to do manually, so the effect of this failover is to route traffic to a FON login page until the user logs in. This is a fairly graceful response to a network outage, and since I mostly use the connection for web browsing I don't see a problem with logging in manually, for my purposes.

Adding automated login and logout: