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

1.1       adi         1: #!/bin/bash
                      2: 
1.8     ! adi         3: # switchhgate 0.0.5
1.7       adi         4: # (c) 2005-2010 under GPL by Adrian Zaugg
1.1       adi         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
1.7       adi        21: ALERT_EMAIL="adi@ente.limmat.ch"
1.2       adi        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.7       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.6       adi        39: DEBUG=
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: 
                     50: # Read gateways from file
                     51: function readgwlist {
                     52:        if [ -f "$GATEWAYS_FILE" ]; then
                     53:                # Ordered list of all gateways in file. Highest preference has entry with index 0, which 
                     54:                # is your standard default gateway.
                     55:                # dhcp[.ethX] means dhcp supplied gateways in file $CONFIG_PATH/dhcp.ethX
                     56:                number_of_gws=0
                     57:                if [ -n "$DEBUG" -a "$DEBUG" != "low" ]; then
                     58:                        echo "Reading config file $GATEWAYS_FILE..."
                     59:                fi
                     60: 
                     61:                allgates=`grep -ve "^[  ]*[\#]\+.*$" "$GATEWAYS_FILE" | xargs`
1.8     ! adi        62:                alldhcpgates=""
1.1       adi        63:                for gate in $allgates; do
1.8     ! adi        64:                        if [[ $(echo "$gate" | grep -c -e "^[[:space:]]*dhcp\..*$") -eq 1 && \
        !            65:                              -f "$DHCP_TMP_DIR/$gate" ]]; then
1.1       adi        66:                                # Read dhcp supplied gate for a specific interface
1.8     ! adi        67:                                alldhcpgates="$alldhcpgates `grep -e "^[^\#].*$" "$DHCP_TMP_DIR/$gate" | xargs`"
        !            68:                        elif [[ $(echo "$gate" | grep -c -e "^[[:space:]]*dhcp[[:space:]]*$") -eq 1 ]]; then
1.1       adi        69:                                # Read all dhcp files
1.8     ! adi        70:                                for dhcpfile in $(echo -n $(ls -1 $DHCP_TMP_DIR/dhcp* 2>/dev/null)); do
1.1       adi        71:                                        alldhcpgates="$alldhcpgates `grep -e "^[^\#].*$" "$dhcpfile" | xargs`"
                     72:                                done
                     73:                        else
                     74:                                # it's a fixed gateway
                     75:                                addgw="$gate"
                     76:                                debug_msg="fixed"
                     77:                                addgateway
                     78:                        fi
                     79: 
                     80:                        for dhcpgate in $alldhcpgates; do
                     81:                                addgw="$dhcpgate"
                     82:                                debug_msg="$gate"
                     83:                                addgateway
                     84:                        done
                     85:                        unset alldhcpgates
                     86:                done
                     87:        fi
1.3       adi        88: 
1.1       adi        89:        if [ "$number_of_gws" -eq 0 ]; then
                     90:                echo "No gateways configured. Please edit $GATEWAYS_FILE."
                     91:                exit 1
                     92:        elif [[ "$number_of_gws" -eq 1 && ! -z $DEBUG ]]; then
                     93:                echo "Only 1 gateway configured. No fallback switching possible."
                     94:        else
                     95:                if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
                     96:                        echo "... $number_of_gws gateway(s) found."
                     97:                fi
                     98:        fi
                     99: }
                    100: 
                    101: # add a gateway to the array of all gateways to possibly switch to
                    102: function addgateway {
                    103:        addgw=$(echo "$addgw" | tr -d ' ')
                    104:        # check no duplicate addition is performed
                    105:        if [ $(echo "${GW[*]}" | grep -c "$addgw") -eq 0 ]; then
                    106:                GW[$number_of_gws]=$addgw
                    107:                if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
                    108:                        echo -e "\tGateway $number_of_gws: $addgw ($debug_msg)"
                    109:                fi
                    110:                let "number_of_gws += 1"
                    111:        fi
                    112:        unset addgw
                    113: }
                    114: 
                    115: # If one host of the HOSTSET_FILE responds, the conncetion is considered up.
                    116: function checkconnection {
                    117:    UP=""
                    118:    if [ `$PING $PINGARGS < "$HOSTSET_FILE" 2> /dev/null | grep -c "$ALIVE_ANSWER"` -gt 0 ]; then
                    119:       UP=true
                    120:    fi
                    121:    if [ -n "$DEBUG" ]; then
                    122:       if [ -n "$UP" ]; then
                    123:          echo "The connection is up."
1.3       adi       124:       else
1.1       adi       125:          echo "The connection is down."
                    126:       fi
                    127:    fi
                    128: }
                    129: 
                    130: # Is a certain gateway reachable?
                    131: function checkgw {
                    132:    if [ `$PING $PINGARGS "$NEWGW" 2> /dev/null | grep -c "$ALIVE_ANSWER"` -gt 0 ]; then
                    133:       if [ -n "$DEBUG" ]; then
                    134:          echo "$NEWGW is reachable."
                    135:       fi
                    136:     else
1.3       adi       137:       if [ -n "$DEBUG" ]; then
1.1       adi       138:           echo "$NEWGW is not reachable."
                    139:       fi
                    140:       NEWGW=""
                    141:    fi
                    142: }
                    143: 
                    144: function getcurrentgw {
                    145:    iproute_default_gw_txt="$(ip route show scope global)"
                    146:    if [ `echo $iproute_default_gw_txt | grep -c default` -gt 1 ]; then
                    147:       echo "No support for multiple default gateways. Exiting."
                    148:       exit 1
                    149:    else
                    150:       iproute_default_gw_txt="$(echo "$iproute_default_gw_txt" | grep "default via")"
1.3       adi       151:    fi
1.1       adi       152:    if [ -z "$iproute_default_gw_txt" ]; then
                    153:       # no default gateway currently set, set to the highest index, to land on GW0
                    154:       CURRENTGW_ID=0
                    155:       CURRENTGW=${GW[$CURRENTGW_ID]}
1.6       adi       156:       CURRENTGW_DEV="unknown"
1.1       adi       157:       echo "No default gateway currently set. Setting it now to $CURRENTGW."
                    158:       ip route add default via $CURRENTGW
                    159:    else
1.3       adi       160:       CURRENTGW=`echo $iproute_default_gw_txt | sed "s/^default via \(\([0-9]\+\.\?\)\{4\}\).*\$/\1/"`
1.6       adi       161:       CURRENTGW_DEV=`echo $iproute_default_gw_txt | sed "s/^default via .* dev \(eth[0-9]\+\).*\$/\1/"`
1.1       adi       162:    fi
                    163: 
                    164:    # get index of current gateway
                    165:    index=-1
                    166:    for NEWGW in ${GW[*]}; do
                    167:       let "index += 1"
                    168:       if [ "$CURRENTGW" = "$NEWGW" ]; then
                    169:          CURRENTGW_ID=$index
                    170:         break
                    171:       fi
                    172:    done
                    173:    if [ -z "$CURRENTGW_ID" ]; then
                    174:       echo "The current gateway ($CURRENTGW) is not in the list of available gateways."
                    175:       echo "Please edit $GATEWAYS_FILE to correct."
                    176:       exit 1
                    177:    fi
                    178:    if [ "$DEBUG" = "low" ]; then
                    179:       echo "Default gateway detected: $CURRENTGW ($CURRENTGW_ID) on dev $CURRENTGW_DEV"
                    180:    fi
                    181: }
                    182: 
                    183: # Set NEWGW and NEWGW_ID to the next gateway from GW[]
                    184: function getnextgw {
                    185:    if [ -z "$NEWGW_ID" ]; then
                    186:       if [ -z "$CURRENTGW_ID" ]; then
                    187:          getcurrentgw
                    188:          NEWGW_ID=$CURRENTGW_ID
                    189:       else
                    190:          NEWGW_ID=-1
                    191:       fi
                    192:    fi
                    193:    NEWGW_ID=$[ $NEWGW_ID + 1 ]
                    194:    if [ "$NEWGW_ID" -ge "${#GW[*]}" ]; then
                    195:       NEWGW_ID=0
                    196:    fi
                    197:    NEWGW=${GW[$NEWGW_ID]}
                    198: }
                    199: 
                    200: # Set $NEWGW to the next working gateway
                    201: function getnewgw {
                    202:    getnextgw
                    203:    # Check wheter all gateways are already tested and failed
1.6       adi       204:    if [ "$NEWGW_ID" -eq "$CURRENTGW_ID" ]; then
1.1       adi       205:       if [ -n "$DEBUG" ]; then
                    206:          echo "All gateways tried."
                    207:       fi
                    208:       NEWGW=""
                    209:       NEWGW_ID=""
                    210:    else
                    211:       if [ -n "$DEBUG" ]; then
                    212:          echo -n "Trying $NEWGW..."
                    213:       fi
                    214:       savegw=$NEWGW
                    215:       checkgw
                    216:       if [ -z "$NEWGW" ]; then
                    217:          if [ -n "$DEBUG" ]; then
                    218:              echo "No luck. Trying next..."
                    219:          fi
                    220:          NEWGW=$savegw
                    221:          getnewgw
                    222:       else
                    223:          if [ -n "$DEBUG" ]; then
                    224:             echo "Successfully found a new gateway, it's $NEWGW."
                    225:          fi
                    226:       fi
                    227:    fi
                    228: }
                    229: 
                    230: function switchgw {
                    231:    getnewgw
                    232:    if [ -n "$NEWGW" ]; then
                    233:       if [ "$DEBUG" = "low" ]; then
                    234:          echo -n "Switching default gateway now to $NEWGW ($NEWGW_ID)..."
1.3       adi       235:       fi
                    236: 
1.1       adi       237:       iproute_msg="$(ip route change default via $NEWGW 2>&1 )"
                    238:       iproute_exit=$?
                    239: 
                    240:       # generate some traffic to take effect
                    241:       sleep 1
                    242:       GARBAGE=`$PING -q -c 5 -t100 -p50 -A -f $HOSTSET_FILE 2> /dev/null`
                    243:       sleep 1
1.3       adi       244: 
1.1       adi       245:       if [ "$DEBUG" = "low" ]; then
                    246:          echo "done."
                    247:       fi
                    248: 
                    249:       checkconnection
                    250:       if [ -z "$UP" ]; then
                    251:          if [ "$DEBUG" = "low" ]; then
                    252:             echo -n "$NEWGW ($NEWGW_ID) is not working, switching back to $CURRENTGW ($CURRENTGW_ID)..."
                    253:         fi
                    254:         ip route change default via $CURRENTGW
                    255:         if [ "$DEBUG" = "low" ]; then
                    256:            echo "done."
                    257:         fi
                    258:         switchgw
                    259:       elif [ "$iproute_exit" -eq 0 ]; then
                    260:          CURRENTGW=$NEWGW
                    261:          CURRENTGW_ID=$NEWGW_ID
                    262:       else
                    263:         echo "Failure from iproute: $iproute_msg"
                    264:         echo "However, the connection is working. Continue as if nothing."
                    265:       fi
                    266:    else
                    267:       if [ "$DEBUG" = "low" ]; then
                    268:          echo "Not switching."
                    269:       fi
                    270:    fi
                    271: }
                    272: 
                    273: 
                    274: ## MAIN
                    275: #
                    276: 
1.6       adi       277: # Parse writeable dir request (hidden feature)
                    278: if [ "$1" = "-C" ]; then
                    279:        echo "$DHCP_TMP_DIR"
                    280:        exit 0
                    281: fi
                    282: 
                    283: # Parse debug switch (another hidden feature)
                    284: if [ "$1" = "-d" ]; then
                    285:        if [ "$2" != "low" ]; then
                    286:                echo "Debug mode on."
                    287:                DEBUG="choke"
                    288:        else
                    289:                #echo "Info mode on."
                    290:                DEBUG="low"
                    291:        fi 
                    292: fi
1.1       adi       293: 
1.3       adi       294: # Check fping existence
                    295: if [ ! -x "$PING" ]; then
                    296:        echo -e "Error: Ping program not executable or not found at $PING.\nPlease configure the full path in $0."
                    297:        exit 1
                    298: fi
                    299: 
1.1       adi       300: # Get all gateways, we can forward traffic
                    301: readgwlist
                    302: 
1.6       adi       303: # Parse query for current gw  (yet another hidden feature)
                    304: if [ "$1" = "-q" ]; then
                    305:        DEBUG=
                    306:        getcurrentgw
                    307:        echo "$CURRENTGW"
                    308:        exit 0
                    309: fi
1.1       adi       310: 
                    311: # get current gw
                    312: getcurrentgw
                    313: 
                    314: # check wether host set file is around
                    315: if [ ! -e $HOSTSET_FILE ]; then
                    316:    echo "No host list found! File $HOSTSET_FILE not found."
                    317:    exit 1
                    318: fi
                    319: 
                    320: # check connection
                    321: checkconnection
                    322: 
                    323: # If the connection is down, change to the nextgw
                    324: if [ -z "$UP" ]; then
                    325:    # Some more information of down reason: Diagnose wheter the current gateway is alive 
                    326:    NEWGW=$CURRENTGW
                    327:    checkgw
                    328:    if [ -z "$NEWGW" ]; then
                    329:       echo "Gateway $CURRENTGW is down."
1.3       adi       330:    else
1.1       adi       331:       echo "No connection through gateway $CURRENTGW." 
                    332:    fi
1.3       adi       333: 
1.1       adi       334:    NEWGW_ID=$CURRENTGW_ID
                    335:    switchgw
1.3       adi       336: 
1.1       adi       337: else
                    338:    # If the current gateway is not the standard default gateway, try if the connection 
                    339:    # through the standard default gateway is back
                    340:    if [ "$CURRENTGW_ID" != 0 ]; then
                    341:       if [ "$DEBUG" = "low" ]; then
                    342:          echo "Not operating on standard default gateway (${GW[0]}). Trying to change."
                    343:       fi
                    344:       NEWGW_ID=""
                    345:       switchgw
                    346:    else
                    347:       # normal operation
                    348:       if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
                    349:          echo "Normal operation on standard default gateway $CURRENTGW ($CURRENTGW_ID)."
                    350:       fi
                    351:    fi
                    352: fi
                    353: 
1.2       adi       354: # send an eMail if the connection is down or limited
                    355: if [ -z "$UP" ]; then
1.6       adi       356:        if [ -z "$NEWGW" ]; then
                    357:                EMAIL_BODY="Switchgate on $(hostname -f) reports: No connection!\n\nTime:\t\t$(date)\nGateway:\t$CURRENTGW ($CURRENTGW_ID)"
                    358:                EMAIL_SUBJECT="Attention: $(hostname -f) has no connection!"
                    359:                echo -e "$EMAIL_BODY" | mail -s "$EMAIL_SUBJECT" "$ALERT_EMAIL"
                    360:                if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
                    361:                        echo "Alert message sent to $ALERT_EMAIL."
                    362:                fi
1.2       adi       363:        fi
                    364: elif [ "$CURRENTGW_ID" != 0 ]; then
1.4       adi       365:        EMAIL_BODY="Switchgate on $(hostname -f) reports: Not operating on standard default gateway!\n\nTime:\t\t$(date)\nGateway:\t$CURRENTGW ($CURRENTGW_ID)"
                    366:        EMAIL_SUBJECT="Attention: $(hostname -f) not operating on standard default gateway!"
1.2       adi       367:        echo -e "$EMAIL_BODY" | mail -s "$EMAIL_SUBJECT" "$ALERT_EMAIL"
                    368:        if [[ -n "$DEBUG" && "$DEBUG" != "low" ]]; then
                    369:                echo "Alert message sent to $ALERT_EMAIL."
                    370:        fi
                    371: fi
                    372: 
1.7       adi       373: if [ "$DEBUG" = "low" -o "$DEBUG" = "choke" ]; then
                    374:        if [ -n "$UP" ]; then
                    375:           echo
                    376:           echo "Current Default Gateway: $CURRENTGW ($CURRENTGW_ID)"
                    377:           echo
                    378:        else
                    379:           echo
                    380:           echo "No connection."
                    381:           echo
                    382:        fi
1.1       adi       383: fi
                    384: exit 0

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