Annotation of badi/public_scripts/switchgate/switchgate, revision 1.5

1.1       adi         1: #!/bin/bash
                      2: 
                      3: # switchhgate 0.0.3
                      4: # (c) 2005 under GPL by Adrian Zaugg
                      5: 
                      6: # switchgate pings a set of hosts to determine the state of the internet connectivity. If
                      7: # the connection is down, it can change the default gateway to an alternative gateway.
                      8: 
                      9: ## Settings
                     10: #
                     11: 
                     12: # Where the config files reside
                     13: CONFIG_PATH="/etc/switchgate"
                     14: 
                     15: # Path where to find dhcp information file from switchgate-helper,
                     16: # the dhclient-exit-hook helper program
                     17: DHCP_TMP_DIR=/tmp/switchgate
                     18: 
1.2       adi        19: # eMail address to send alert messages, if not operating on the
                     20: # standard default gateway
                     21: ALERT_EMAIL="adi@ente.limmat.ch"
                     22: 
1.1       adi        23: # Host set to ping. A file containing IP addresses, each on a single line.
                     24: # Do not include any local hosts, list only hosts located behind your gateways.
                     25: HOSTSET_FILE="$CONFIG_PATH/hostset"
                     26: 
                     27: # The file defining fixed and dynamic gateway preference
                     28: GATEWAYS_FILE="$CONFIG_PATH/gateways"
                     29: 
                     30: # path to fping
1.3       adi        31: PING=/usr/bin/fping
1.1       adi        32: 
                     33: # answer of fping to reachable hosts
                     34: ALIVE_ANSWER="is alive"
                     35: 
                     36: # set to an empty string to avoid debug output
                     37: # to "low" for a few output and to anything else
                     38: # for verbose output
1.5     ! adi        39: DEBUG=low
1.1       adi        40: 
                     41: 
                     42: # -------- Do not edit below this line --------
                     43: 
                     44: PINGARGS="-A -p25 -t100"
                     45: 
                     46: 
                     47: ## Function declarations
                     48: #
                     49: 
1.5     ! adi        50: # parse commandline switches
        !            51: function parse_commandline {
        !            52:        
        !            53:        # check rest
        !            54:        while [ "$#" -gt 0 ]; do
        !            55: 
        !            56:           case "$1" in
        !            57: 
        !            58:           -d|--debug)
        !            59:                # debug switch (another hidden feature)
        !            60:                shift
        !            61:                if [ "$1" != "low" ]; then
        !            62:                        DEBUG="choke"
        !            63:                        echo "Debug mode on."
        !            64:                else
        !            65:                        #echo "Info mode on."
        !            66:                        DEBUG="low"
        !            67:                        shift
        !            68:                fi
        !            69:                ;;
        !            70: 
        !            71:           -C)
        !            72:                # Parse writeable dir request (hidden feature)
        !            73:                echo "$DHCP_TMP_DIR"
        !            74:                exit 0
        !            75:                ;;
        !            76: 
        !            77:           -q|--query)
        !            78:                # Parse query for current gw  (yet another hidden feature)
        !            79:                shift
        !            80:                parse_commandline "$@"
        !            81:                getcurrentgw
        !            82:                echo "$CURRENTGW"
        !            83:                exit 0
        !            84:                ;;
        !            85: 
        !            86:           -s|--set)
        !            87:                # Set gateway to number n (yet another hidden feature)
        !            88:                shift
        !            89:                set_to_gw="$1"
        !            90:                shift
        !            91:                parse_commandline "$@"
        !            92:                set_gw
        !            93:                exit 0
        !            94:                ;;
        !            95: 
        !            96:           *)
        !            97:                echo "Invalid argument \"$1\"."
        !            98:                exit 1
        !            99:                ;;
        !           100: 
        !           101:           esac
        !           102:        done
        !           103: }
        !           104: 
1.1       adi       105: # Read gateways from file
                    106: function readgwlist {
                    107:        if [ -f "$GATEWAYS_FILE" ]; then
                    108:                # Ordered list of all gateways in file. Highest preference has entry with index 0, which 
                    109:                # is your standard default gateway.
                    110:                # dhcp[.ethX] means dhcp supplied gateways in file $CONFIG_PATH/dhcp.ethX
                    111:                number_of_gws=0
                    112:                if [ -n "$DEBUG" -a "$DEBUG" != "low" ]; then
                    113:                        echo "Reading config file $GATEWAYS_FILE..."
                    114:                fi
                    115: 
                    116:                allgates=`grep -ve "^[  ]*[\#]\+.*$" "$GATEWAYS_FILE" | xargs`
                    117:                for gate in $allgates; do
                    118:                        if [[ $(echo "$gate" | grep -c -e "^[:space:]*dhcp\..*$") -eq 1 && \
                    119:                              -f "$CONFIG_PATH/$gate" ]]; then
                    120:                                # Read dhcp supplied gate for a specific interface
                    121:                                alldhcpgates=`grep -e "^[^\#].*$" "$CONFIG_PATH/$gate" | xargs`
                    122:                        elif [[ $(echo "$gate" | grep -c -e "^[:space:]*dhcp[:space:]*$") -eq 1 ]]; then
                    123:                                # Read all dhcp files
                    124:                                alldhcpgates=""
                    125:                                for dhcpfile in $(echo -n $(ls -1 $DHCP_TMP_DIR/dhcp* 2>/dev/null)); do 
                    126:                                        alldhcpgates="$alldhcpgates `grep -e "^[^\#].*$" "$dhcpfile" | xargs`"
                    127:                                done
                    128:                        else
                    129:                                # it's a fixed gateway
                    130:                                addgw="$gate"
                    131:                                debug_msg="fixed"
                    132:                                addgateway
                    133:                        fi
                    134: 
                    135:                        for dhcpgate in $alldhcpgates; do
                    136:                                addgw="$dhcpgate"
                    137:                                debug_msg="$gate"
                    138:                                addgateway
                    139:                        done
                    140:                        unset alldhcpgates
                    141:                done
                    142:        fi
1.3       adi       143: 
1.1       adi       144:        if [ "$number_of_gws" -eq 0 ]; then
                    145:                echo "No gateways configured. Please edit $GATEWAYS_FILE."
                    146:                exit 1
                    147:        elif [[ "$number_of_gws" -eq 1 && ! -z $DEBUG ]]; then
                    148:                echo "Only 1 gateway configured. No fallback switching possible."
                    149:        else
                    150:                if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
                    151:                        echo "... $number_of_gws gateway(s) found."
                    152:                fi
                    153:        fi
                    154: }
                    155: 
                    156: # add a gateway to the array of all gateways to possibly switch to
                    157: function addgateway {
                    158:        addgw=$(echo "$addgw" | tr -d ' ')
                    159:        # check no duplicate addition is performed
                    160:        if [ $(echo "${GW[*]}" | grep -c "$addgw") -eq 0 ]; then
                    161:                GW[$number_of_gws]=$addgw
                    162:                if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
                    163:                        echo -e "\tGateway $number_of_gws: $addgw ($debug_msg)"
                    164:                fi
                    165:                let "number_of_gws += 1"
                    166:        fi
                    167:        unset addgw
                    168: }
                    169: 
                    170: # If one host of the HOSTSET_FILE responds, the conncetion is considered up.
                    171: function checkconnection {
                    172:    UP=""
                    173:    if [ `$PING $PINGARGS < "$HOSTSET_FILE" 2> /dev/null | grep -c "$ALIVE_ANSWER"` -gt 0 ]; then
                    174:       UP=true
                    175:    fi
                    176:    if [ -n "$DEBUG" ]; then
                    177:       if [ -n "$UP" ]; then
                    178:          echo "The connection is up."
1.3       adi       179:       else
1.1       adi       180:          echo "The connection is down."
                    181:       fi
                    182:    fi
                    183: }
                    184: 
                    185: # Is a certain gateway reachable?
                    186: function checkgw {
                    187:    if [ `$PING $PINGARGS "$NEWGW" 2> /dev/null | grep -c "$ALIVE_ANSWER"` -gt 0 ]; then
                    188:       if [ -n "$DEBUG" ]; then
                    189:          echo "$NEWGW is reachable."
                    190:       fi
                    191:     else
1.3       adi       192:       if [ -n "$DEBUG" ]; then
1.1       adi       193:           echo "$NEWGW is not reachable."
                    194:       fi
                    195:       NEWGW=""
                    196:    fi
                    197: }
                    198: 
1.5     ! adi       199: # Get the current default gateway (or set it, if there was none set) 
1.1       adi       200: function getcurrentgw {
                    201:    iproute_default_gw_txt="$(ip route show scope global)"
                    202:    if [ `echo $iproute_default_gw_txt | grep -c default` -gt 1 ]; then
                    203:       echo "No support for multiple default gateways. Exiting."
                    204:       exit 1
                    205:    else
                    206:       iproute_default_gw_txt="$(echo "$iproute_default_gw_txt" | grep "default via")"
1.3       adi       207:    fi
1.1       adi       208:    if [ -z "$iproute_default_gw_txt" ]; then
                    209:       # no default gateway currently set, set to the highest index, to land on GW0
                    210:       CURRENTGW_ID=0
                    211:       CURRENTGW=${GW[$CURRENTGW_ID]}
                    212:       echo "No default gateway currently set. Setting it now to $CURRENTGW."
                    213:       ip route add default via $CURRENTGW
1.5     ! adi       214:       if [ $? -eq 2 ]; then
        !           215:        echo "Not able to switch default gateway. Is your interface up?"
        !           216:        exit 1
        !           217:       fi
1.1       adi       218:    else
1.3       adi       219:       CURRENTGW=`echo $iproute_default_gw_txt | sed "s/^default via \(\([0-9]\+\.\?\)\{4\}\).*\$/\1/"`
1.1       adi       220:    fi
1.5     ! adi       221:    CURRENTGW_DEV=`echo $iproute_default_gw_txt | sed "s/^default via .* dev \(eth[0-9]\+\).*\$/\1/"`
1.1       adi       222: 
                    223:    # get index of current gateway
                    224:    index=-1
                    225:    for NEWGW in ${GW[*]}; do
                    226:       let "index += 1"
                    227:       if [ "$CURRENTGW" = "$NEWGW" ]; then
                    228:          CURRENTGW_ID=$index
                    229:         break
                    230:       fi
                    231:    done
                    232:    if [ -z "$CURRENTGW_ID" ]; then
                    233:       echo "The current gateway ($CURRENTGW) is not in the list of available gateways."
                    234:       echo "Please edit $GATEWAYS_FILE to correct."
                    235:       exit 1
                    236:    fi
                    237:    if [ "$DEBUG" = "low" ]; then
                    238:       echo "Default gateway detected: $CURRENTGW ($CURRENTGW_ID) on dev $CURRENTGW_DEV"
                    239:    fi
                    240: }
                    241: 
                    242: # Set NEWGW and NEWGW_ID to the next gateway from GW[]
                    243: function getnextgw {
                    244:    if [ -z "$NEWGW_ID" ]; then
                    245:       if [ -z "$CURRENTGW_ID" ]; then
                    246:          getcurrentgw
                    247:          NEWGW_ID=$CURRENTGW_ID
                    248:       else
                    249:          NEWGW_ID=-1
                    250:       fi
                    251:    fi
                    252:    NEWGW_ID=$[ $NEWGW_ID + 1 ]
                    253:    if [ "$NEWGW_ID" -ge "${#GW[*]}" ]; then
                    254:       NEWGW_ID=0
                    255:    fi
                    256:    NEWGW=${GW[$NEWGW_ID]}
                    257: }
                    258: 
                    259: # Set $NEWGW to the next working gateway
                    260: function getnewgw {
                    261:    getnextgw
                    262:    # Check wheter all gateways are already tested and failed
1.5     ! adi       263:    if [[ "$NEWGW_ID" -eq "$CURRENTGW_ID" &&  -z $set_to_gw ]]; then
1.1       adi       264:       if [ -n "$DEBUG" ]; then
                    265:          echo "All gateways tried."
                    266:       fi
                    267:       NEWGW=""
                    268:       NEWGW_ID=""
                    269:    else
                    270:       if [ -n "$DEBUG" ]; then
                    271:          echo -n "Trying $NEWGW..."
                    272:       fi
                    273:       savegw=$NEWGW
                    274:       checkgw
                    275:       if [ -z "$NEWGW" ]; then
                    276:          if [ -n "$DEBUG" ]; then
                    277:              echo "No luck. Trying next..."
                    278:          fi
                    279:          NEWGW=$savegw
                    280:          getnewgw
                    281:       else
                    282:          if [ -n "$DEBUG" ]; then
                    283:             echo "Successfully found a new gateway, it's $NEWGW."
                    284:          fi
                    285:       fi
                    286:    fi
                    287: }
                    288: 
1.5     ! adi       289: # Switch the gateway to a desired gateway
        !           290: function set_gw {
        !           291: 
        !           292:    # get name of gateway
        !           293:    if [[ ! "$set_to_gw" =~ ^[0-9]{1,}$ ]]; then
        !           294:        echo "Only positive numbers allowed. Invalid value \"$set_to_gw\"."
        !           295:        exit 1   
        !           296:    fi
        !           297:    
        !           298:    NEWGW=${GW[$set_to_gw]}
        !           299:    if [ -z "$NEWGW" ]; then
        !           300:       echo "The gateway number $set_to_gw is not in the list of available gateways."
        !           301:       echo "Valid gateways are:"
        !           302:       for ((gw=0; gw<${#GW[*]}; gw++)); do
        !           303:          echo -e "\t$gw (${GW[$gw]})"
        !           304:       done 
        !           305:       exit 1
        !           306:    fi
        !           307:    if [ -n "$DEBUG" -a "$DEBUG" != "low" ]; then
        !           308:       echo "Default gateway desired: $NEWGW ($set_to_gw)"
        !           309:    fi
        !           310:    
        !           311:    getcurrentgw
        !           312:    NEWGW_ID=$[ $set_to_gw - 1 ]
        !           313:    switchgw
        !           314: }
        !           315: 
1.1       adi       316: function switchgw {
                    317:    getnewgw
                    318:    if [ -n "$NEWGW" ]; then
                    319:       if [ "$DEBUG" = "low" ]; then
                    320:          echo -n "Switching default gateway now to $NEWGW ($NEWGW_ID)..."
1.3       adi       321:       fi
                    322: 
1.1       adi       323:       iproute_msg="$(ip route change default via $NEWGW 2>&1 )"
                    324:       iproute_exit=$?
                    325: 
                    326:       # generate some traffic to take effect
                    327:       sleep 1
                    328:       GARBAGE=`$PING -q -c 5 -t100 -p50 -A -f $HOSTSET_FILE 2> /dev/null`
                    329:       sleep 1
1.3       adi       330: 
1.1       adi       331:       if [ "$DEBUG" = "low" ]; then
                    332:          echo "done."
                    333:       fi
                    334: 
                    335:       checkconnection
                    336:       if [ -z "$UP" ]; then
                    337:          if [ "$DEBUG" = "low" ]; then
                    338:             echo -n "$NEWGW ($NEWGW_ID) is not working, switching back to $CURRENTGW ($CURRENTGW_ID)..."
                    339:         fi
                    340:         ip route change default via $CURRENTGW
                    341:         if [ "$DEBUG" = "low" ]; then
                    342:            echo "done."
                    343:         fi
                    344:         switchgw
                    345:       elif [ "$iproute_exit" -eq 0 ]; then
                    346:          CURRENTGW=$NEWGW
                    347:          CURRENTGW_ID=$NEWGW_ID
                    348:       else
                    349:         echo "Failure from iproute: $iproute_msg"
                    350:         echo "However, the connection is working. Continue as if nothing."
                    351:       fi
                    352:    else
                    353:       if [ "$DEBUG" = "low" ]; then
                    354:          echo "Not switching."
                    355:       fi
                    356:    fi
                    357: }
                    358: 
                    359: 
                    360: ## MAIN
                    361: #
                    362: 
                    363: 
1.3       adi       364: # Check fping existence
                    365: if [ ! -x "$PING" ]; then
                    366:        echo -e "Error: Ping program not executable or not found at $PING.\nPlease configure the full path in $0."
                    367:        exit 1
                    368: fi
                    369: 
1.1       adi       370: # Get all gateways, we can forward traffic
                    371: readgwlist
                    372: 
1.5     ! adi       373: # parse commandline switches
        !           374: parse_commandline "$@"
1.1       adi       375: 
                    376: # get current gw
                    377: getcurrentgw
                    378: 
                    379: # check wether host set file is around
                    380: if [ ! -e $HOSTSET_FILE ]; then
                    381:    echo "No host list found! File $HOSTSET_FILE not found."
                    382:    exit 1
                    383: fi
                    384: 
                    385: # check connection
                    386: checkconnection
                    387: 
                    388: # If the connection is down, change to the nextgw
                    389: if [ -z "$UP" ]; then
                    390:    # Some more information of down reason: Diagnose wheter the current gateway is alive 
                    391:    NEWGW=$CURRENTGW
                    392:    checkgw
                    393:    if [ -z "$NEWGW" ]; then
                    394:       echo "Gateway $CURRENTGW is down."
1.3       adi       395:    else
1.1       adi       396:       echo "No connection through gateway $CURRENTGW." 
                    397:    fi
1.3       adi       398: 
1.1       adi       399:    NEWGW_ID=$CURRENTGW_ID
                    400:    switchgw
1.3       adi       401: 
1.1       adi       402: else
                    403:    # If the current gateway is not the standard default gateway, try if the connection 
                    404:    # through the standard default gateway is back
                    405:    if [ "$CURRENTGW_ID" != 0 ]; then
                    406:       if [ "$DEBUG" = "low" ]; then
                    407:          echo "Not operating on standard default gateway (${GW[0]}). Trying to change."
                    408:       fi
                    409:       NEWGW_ID=""
                    410:       switchgw
                    411:    else
                    412:       # normal operation
                    413:       if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
                    414:          echo "Normal operation on standard default gateway $CURRENTGW ($CURRENTGW_ID)."
                    415:       fi
                    416:    fi
                    417: fi
                    418: 
1.2       adi       419: # send an eMail if the connection is down or limited
                    420: if [ -z "$UP" ]; then
1.4       adi       421:        EMAIL_BODY="Switchgate on $(hostname -f) reports: No connection!\n\nTime:\t\t$(date)\nGateway:\t$CURRENTGW ($CURRENTGW_ID)"
1.2       adi       422:        EMAIL_SUBJECT="Attention: $(hostname -f) has no connection!"
                    423:        echo -e "$EMAIL_BODY" | mail -s "$EMAIL_SUBJECT" "$ALERT_EMAIL"
                    424:        if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
                    425:                echo "Alert message sent to $ALERT_EMAIL."
                    426:        fi
                    427: elif [ "$CURRENTGW_ID" != 0 ]; then
1.4       adi       428:        EMAIL_BODY="Switchgate on $(hostname -f) reports: Not operating on standard default gateway!\n\nTime:\t\t$(date)\nGateway:\t$CURRENTGW ($CURRENTGW_ID)"
                    429:        EMAIL_SUBJECT="Attention: $(hostname -f) not operating on standard default gateway!"
1.2       adi       430:        echo -e "$EMAIL_BODY" | mail -s "$EMAIL_SUBJECT" "$ALERT_EMAIL"
                    431:        if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
                    432:                echo "Alert message sent to $ALERT_EMAIL."
                    433:        fi
                    434: fi
                    435: 
1.1       adi       436: if [ "$DEBUG" = "low" ]; then
                    437:    echo
                    438:    echo "Default Gateway: $CURRENTGW ($CURRENTGW_ID)"
                    439:    echo
                    440: fi
                    441: exit 0

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>