diff --git a/routers/README b/routers/README new file mode 100644 index 0000000..b889e69 --- /dev/null +++ b/routers/README @@ -0,0 +1,66 @@ +Redundant routers control +========================== + +I'm using two Linux routers at home, along with a manageable switch and an ADSL +modem. + +Both routers have their own address, and so does the switch. When one of the +routers is active, it uses an additional address which serves as the default +gateway for the rest of the boxes. + +One of the routers is an old box; it's rather slow, doesn't have much memory, +and more importantly, it's dying. Obviously, it should only be used when the +other one is down. However, if the connection has been established by that +secondary router, the primary should not take over until the DSL link goes down +on its own. + +The scripts in this directory are used to control which router is active. + + +Checks on the primary router +----------------------------- + +When the primary router is the active router: + + 1/ Check the switch + 2/ If the switch is down, deactivate. + +When the primary router is inactive: + + 1/ Check the switch and the main router address + 2/ If the switch is up and the main router does not exist, activate. + + +Checks on the secondary router +------------------------------- + +When the secondary router is the active router: + + 1/ Check the switch, the primary router, and a remote server + 2/ If the remote server is reachable, do nothing + 3/ If the switch is down, or if the primary router is up, deactivate. + +When the secondary router is inactive: + + 1/ Check the switch, the primary router, and the main router address + 2/ If the switch is up, and both the primary router and the main router + address are down, activate. + + +Installation +------------- + +1/ Copy router-checks.conf to /etc, modify it however you want. + +2/ Copy share/ to /usr/local/share/router-checks (or to whatever you set the + LIB_DIR configuration variable to). + +3/ Modify activate.sh and deactivate.sh; they determine how a router becomes + the primary router and how a router releases primary routing control, + respectively. + +4/ Copy primary-router.sh or secondary-router.sh to + /usr/local/sbin/router-checks + +5/ If necessary (and if it will work on whatever distribution you use), use + the provided init script to start the checks on boot. diff --git a/routers/init-script b/routers/init-script new file mode 100644 index 0000000..a01881c --- /dev/null +++ b/routers/init-script @@ -0,0 +1,67 @@ +#!/bin/sh +# +### BEGIN INIT INFO +# Provides: router-checks +# Required-Start: $local_fs networking +# Required-Stop: $local_fs networking +# Should-Start: iptables-persistent ip6tables-persistent +# Should-Stop: iptables-persistent ip6tables-persistent +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Router control +### END INIT INFO + +. /lib/lsb/init-functions +. /etc/router-checks.conf + +case "$1" in + start) + log_daemon_msg "Starting router control..." "router-checks" + if [ -f $PID_FILE ]; then + log_action_msg "PID file exists" + log_end_msg 1 + fi + /usr/local/sbin/router-checks /dev/null 2>&1 + sleep 1 + if [ -f $PID_FILE ]; then + log_end_msg 0 + else + log_end_msg 1 + fi + ;; + + stop) + log_daemon_msg "Stopping router control..." "router-checks" + if ! [ -f $PID_FILE ]; then + log_action_msg "PID file not found" + log_end_msg 1 + fi + rm -f $PID_FILE.`cat $PID_FILE` + sleep 2 + if [ -f "$PID_FILE" ]; then + count=18 + log_action_begin_msg " Waiting" + while [ $count -gt 0 ] && [ -f "$PID_FILE" ]; do + sleep 5 + log_action_cont_msg "" + count=$(( count - 1 )) + done + if [ -f "$PID_FILE" ]; then + log_end_msg 1 + fi + fi + log_end_msg 0 + ;; + + restart) + $0 stop + $0 start + ;; + + *) + echo "Usage: $0 {start|stop|restart|force-reload|status}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/routers/primary-router.sh b/routers/primary-router.sh new file mode 100644 index 0000000..ae5e9fd --- /dev/null +++ b/routers/primary-router.sh @@ -0,0 +1,63 @@ +#!/bin/bash +( + +. /etc/router-checks.conf + +exec >>$LOG_FILE 2>&1 +date +echo "... STARTING" +. "$LIB_DIR/pings" + +echo $$ > $PID_FILE +stop_file="$PID_FILE.$$" +trap "rm -f $stop_file" SIGINT SIGTERM +touch $stop_file +SLEEP_TIME=60 +while [ -f "$stop_file" ]; do + sleep $SLEEP_TIME + SLEEP_TIME=1 + + pr_switch=`mktemp` + if [ -f "$ACTIVATION_FILE" ]; then + + # When the primary router is active, disable it if the switch + # is off-line + + wait_pings `try_ping $SWITCH_ADDR $pr_switch` + if [ -f "$pr_switch" ]; then + date + echo "de-activating connection" + $LIB_DIR/deactivate.sh + rm -f "$ACTIVATION_FILE" + SLEEP_TIME=10 + fi + + else + + # When the primary router is inactive, enable it if the switch + # is up and the main router IP does not exist + + pr_main=`mktemp` + wait_pings `try_ping $SWITCH_ADDR $pr_switch` \ + `try_ping $MAIN_ADDR $pr_main` + if [ -f "$pr_main" ] && ! [ -f "$pr_switch" ]; then + date + echo "activating connection" + $LIB_DIR/activate.sh + touch "$ACTIVATION_FILE" + SLEEP_TIME=20 + fi + rm -f "$pr_main" + + fi + rm -f "$pr_switch" +done +if [ -f "$ACTIVATION_FILE" ]; then + $LIB_DIR/deactivate.sh + rm -f "$ACTIVATION_FILE" +fi +date +echo "... EXITING" +rm "$PID_FILE" + +) & diff --git a/routers/router-checks.conf b/routers/router-checks.conf new file mode 100644 index 0000000..1783dfe --- /dev/null +++ b/routers/router-checks.conf @@ -0,0 +1,25 @@ +# Router checks configuration + +# Some remote address to ping +INTERNET_ADDR="173.194.34.1" + +# Main router address +MAIN_ADDR="192.168.1.1" + +# Primary router's own address +PRIMARY_ADDR="192.168.1.1" + +# Address of the switch +SWITCH_ADDR="192.168.1.4" + +# PID files +PID_FILE="/var/run/router-check" + +# Activation file, used to keep track of state +ACTIVATION_FILE="/var/run/router-active" + +# Library directory - where scripts reside +LIB_DIR="/usr/local/share/router-checks" + +# Log file +LOG_FILE="/var/log/router-checks.log" diff --git a/routers/secondary-router.sh b/routers/secondary-router.sh new file mode 100644 index 0000000..8393fe4 --- /dev/null +++ b/routers/secondary-router.sh @@ -0,0 +1,73 @@ +#!/bin/bash +( + +. /etc/router-checks.conf + +exec >>$LOG_FILE 2>&1 +date +echo "... STARTING" +. "$LIB_DIR/pings" + +echo $$ > $PID_FILE +stop_file="$PID_FILE.$$" +trap "rm -f $stop_file" SIGINT SIGTERM +touch $stop_file +SLEEP_TIME=60 +while [ -f "$stop_file" ]; do + sleep $SLEEP_TIME + SLEEP_TIME=1 + + pr_switch=`mktemp` + pr_peer=`mktemp` + if [ -f "$ACTIVATION_FILE" ]; then + + # When the secondary router is active, disable it if the + # internet is not reachable and either the primary router is + # up or the switch is down + + pr_internet=`mktemp` + wait_pings `try_ping $INTERNET_ADDR $pr_internet` \ + `try_ping $PRIMARY_ADDR $pr_peer` \ + `try_ping $SWITCH_ADDR $pr_switch` + if [ -f "$pr_internet" ]; then + if [ -f "$pr_switch" ] || ! [ -f "$pr_peer" ]; then + date + echo "de-activating connection" + $LIB_DIR/deactivate.sh + rm -f "$ACTIVATION_FILE" + SLEEP_TIME=10 + fi + fi + rm -f "$pr_internet" + + else + + # When the secondary router is inactive, enable it if the + # switch is up and both the primary router and the main address + # are unreachable + + pr_main=`mktemp` + wait_pings `try_ping $SWITCH_ADDR $pr_switch` \ + `try_ping $PRIMARY_ADDR $pr_peer` \ + `try_ping $MAIN_ADDR $pr_main` + if [ -f "$pr_main" ] && [ -f "$pr_peer" ] && ! [ -f "$pr_switch" ]; then + date + echo "activating connection" + $LIB_DIR/activate.sh + touch "$ACTIVATION_FILE" + SLEEP_TIME=20 + fi + rm -f "$pr_main" + + fi + rm -f "$pr_peer" "$pr_switch" +done +if [ -f "$ACTIVATION_FILE" ]; then + $LIB_DIR/deactivate.sh + rm -f "$ACTIVATION_FILE" +fi +date +echo "... EXITING" +rm "$PID_FILE" + +) & diff --git a/routers/share/activate.sh b/routers/share/activate.sh new file mode 100644 index 0000000..e825098 --- /dev/null +++ b/routers/share/activate.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# +# Example router activation script +# +# 1/ Remove default route through peer +# 2/ Take active router address +# 3/ Activate PPP link +# 4/ Wait for a few seconds +# + +ip route del default via 192.168.1.1 dev eth0 +ip addr add 192.168.1.1/24 dev eth0 +ifup ppp0 +sleep 10 diff --git a/routers/share/deactivate.sh b/routers/share/deactivate.sh new file mode 100644 index 0000000..b2520f1 --- /dev/null +++ b/routers/share/deactivate.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# +# Example router de-activation script +# +# 1/ Kill internet link +# 2/ Nuke them from orbit, it's the only way to be sure +# 3/ Release main router address +# 4/ Add default route through peer +# + +ifdown --force ppp0 +sleep 2 +killall -9 pppd +sleep 2 + +ip addr del 192.168.1.1 dev eth0 +ip route add default via 192.168.1.1 dev eth0 diff --git a/routers/share/pings b/routers/share/pings new file mode 100644 index 0000000..b1f1aef --- /dev/null +++ b/routers/share/pings @@ -0,0 +1,29 @@ +#!/bin/bash + +try_ping() +{ + export target="$1" + export prfile="$2" + export endfile=`mktemp` + ( + if ping -c1 $target >/dev/null 2>&1; then + rm -f $prfile + fi + rm -f $endfile + ) >/dev/null 2>&1 & + echo $endfile +} + +wait_pings() +{ + local ok=0 + while [ "$ok" = "0" ]; do + sleep 1 + ok=1 + for file in $*; do + if [ -f "$file" ]; then + ok=0 + fi + done + done +}