I found no reliable way to detect if the Internet connection is down, other than periodically pinging a bunch of external hosts that are known to be up most of the time. (The status of the interface is UP all the time).
To know whether your link is active, send an ICMP echo request (also known as "ping") to a known-reliable host at the other end of the link you are trying to test. Usually this will be the ISP's default gateway, but there are other possibilities. If you cannot use the ISP's default gateway for such testing, I'd suggest targetting your ISP's DNS servers; if they are down, things are pretty bad anyway and if you don't need for this to be 100% accurate, that's probably good enough. Remember IP is best-effort only, and ICMP does not have TCP's connection-oriented nature and guarantees.
Don't flood a host with pings unless you have a mutual agreement with the administrator of the remote system. Once every 30 seconds is probably quite enough, possibly increasing when the link is detected as down and decreasing during periods of low use such as during the night. The interval should be chosen based on how quickly you want to respond to a link-down situation and what the remote server's administrator is likely to tolerate. More than one ping every few seconds is almost certainly over the top, and if you don't need an "immediate" response to a link-down situation there's nothing saying you can't test once every few minutes or even more seldom.
What tool should I use for redirection? Iptables? Something else?
Since you are talking about putting up a web page when the link is down, I suspect that you are mostly concerned with web browsing. When browsing the web, using IP addresses directly is the exception.
So set up a local caching DNS resolver, and point your clients to it. Set it to forward to your ISP's DNS servers, or set it to go out and fetch answers on its own based on root hints, that's up to you.
When the link fails, swap out the configuration for the local DNS server for one that is authoritative for the root zone .
and answers any A
(and possibly AAAA
) queries to it with the IP address of your local web server, and issue a configuration reload command. Make sure the local web server is not differentiating based on the requested host name (put the web page in question on the default virtual host). You'd have something like the following in a BIND configuration that gets swapped in when you detect a link failure:
zone "." { type master; file "failed-connection.root.zone"; };
and then in failed-connection.root.zone
you'd have:
$ORIGIN .
$TTL 10
@ SOA <your SOA record details here>
* A 192.168.9.10
* AAAA fe80:123:45::1
Make sure to use a short TTL (I used 10 seconds in the above example) to avoid inadvertant caching of the "failure" response. Also make sure to use IP addresses that do not depend on external connectivity. (Strictly speaking fe80::/16 is deprecated, but it's good enough for illustrative purposes.) The "link down detected" script may also need to flush the DNS server's cache. Also, make very sure that this does not leak onto the Internet. In BIND 9, make good friends with the views feature; with other software, investigate alternatives before making something like this live.
When you detect the link coming back up, just put back the original BIND (or other DNS server) configuration file and issue another configuration reload command and possibly cache flush.
You could of course use e.g. iptables with pre-routing address rewriting, but then you'd risk needing something that can handle basically anything anyone might want to throw at anything on the Internet, for marginal additional utility. To me, doing it that way doesn't seem worth the potential trouble.