-#!/bin/sh
-#
-# IPFire script - vpn-watch
-#
-# This code is distributed under the terms of the GPL
-#
-# (c) Daniel Berlin <daniel berlin_itechnology de> - Check for
-# remote peer with dynamic IPs and restart when change
-# is detected. Works with DPD which is not perfect!
-#
-# 2006: Franck - adapted original script to fit in IPCop 1.4
-# 2007: Michael Tremer - mitch@ipfire.org - Merged into IPFire
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-
-#
-# Configuration
-#
-
-VPN_CONFIG='/var/ipfire/vpn/config' # Location of IPFire's vpn configuration file
-SETTINGS='/var/ipfire/vpn/settings' # and settings
-
-CHECK_INTERVAL='60' # Check this often (in seconds)
-DNS_RESOLVE_TRIES='4' # Try to resolve IPs this often (each try takes max. 2 seconds)
-NICENESS='+5' # Adjust niceness of child processes: '-20' ... '+19'; '0' is default
-case "$1" in
- 'start' | '--start')
- eval $(/usr/local/bin/readhash $SETTINGS)
- test "${VPN_WATCH}" != "on" && exit 1 # not activated, cannot start!
-
- if test ! -r "$VPN_CONFIG"; then
- echo 'Error: cannot read IPFire VPN configuration file; exit.' >&2
- exit 1
- fi
-
- if /usr/bin/test -p /var/run/$(basename $0); then
- if ps --no-heading axw | grep -v 'grep' | grep -q "$(basename $0) conn: "; then
- echo "Error: use '$(basename $0) stop' please; exit." >&2
- exit 1
- else
- rm /var/run/$(basename $0) # pipe was left alone, correct error condition
- fi
- fi
-
- # the pipe serves for "-status" but is not used yet
- /bin/mknod -m 0660 "/var/run/$(basename $0)" p >/dev/null 2>&1 # Create pipe for status-information
-
- #
- # Read VPN configuration and fork a child process for each VPN connection active, net-to-net & RED
- #
- while read line; do
- VPN=($(echo $line | cut --delimiter=',' --output-delimiter=' ' -f2,3,5,12,28 )) # Activated, Name, Host/Net-to-net, Remote, ITF.
- test "${VPN[0]}" != "on" && continue # Ignore: deactivated connections
- test "${VPN[2]}" = "host" && continue # Ignore: roadwarriors
- ## test "${VPN[4]}" != "RED" && continue # Ignore: local vpns needed or not ?
- echo -n "${VPN[3]}" | grep -q '^[[:digit:]\.]\+$' && continue #If fixed remote IP, no need to watch!
- $0 'conn:' "${VPN[1]}" "${VPN[3]}">/dev/null 2>&1 & #Fork child process (parameters: "conn: NAME RIGHT")
- done < "$VPN_CONFIG"
- exit 0 # Parent dies here... RIP
- ;;
-
- 'stop' | '--stop')
- # Terminate processes
- for proc in $(pidof -x -o %PPID $(basename $0)); do
- kill -s SIGTERM -- "$proc"
- done
- sleep 1
-
- # Kill remaining processes
- for proc in $(/bin/pidof -x -o %PPID $(basename $0)); do
- kill -s SIGKILL -- "$proc"
- done
- rm -f "/var/run/$(basename $0)" # Remove pipe
- exit 0
- ;;
-
- #'status' | '--status')
- # echo "VPN-Watch"
- # if ps --no-heading axw | grep -v 'grep' | grep -q "$(basename $0) conn: "; then
- # trap '' USR1
- # killall -q -g -s USR1 -- $(basename $0)
- # sleep 1
- # cat "/var/run/$(basename $0)" | sort # Read children's info from pipe
- # else
- # echo ' no instance running.'
- # fi
- # exit 0
- # ;;
-
- 'conn:')
- # Children proceed here...
- renice ${NICENESS:-0} -p $$ >/dev/null 2>&1 # Adjust niceness
- shift # Remove the first positional parameter ("conn:"), as we don't need it anymore
- ;;
- *)
- /bin/echo "Usage: $0 { start | stop }" >&2
- exit 1
- ;;
-esac
-
-# Logging, signal handlers
-alias log="logger -t vpn-watch \'${1}\':"
-
-trap 'log "terminated after ${RESTART_COUNT} restarts."' EXIT
-#trap 'echo "connection \"${1}\" restarted ${RESTART_COUNT} times" >>/var/run/$(basename $0)' USR1
-
-#
-# Get IP of a FQDN... using 'host' command. Everything is ok when dns server responds.
-# If no response,
-# -maybe RED is down. The script can terminate. It will restart with rc.updatered.
-# or
-# -the dns server is down. In this case, terminate the script is not a good idea...
-# Thus 4 retries before returning response 'stop'
-#
-function get_ip () {
- local RESULT=''
- # delay divided by two for each loop
- delay=8
- for ((i=1; ${i} <= ${DNS_RESOLVE_TRIES}; i++)); do
-
- # extract IP address
- RESULT=$(/usr/bin/host "$1" 2>/dev/null| awk '{ print $4 }')
- if echo -n $RESULT | /bin/grep -q '^[[:digit:]\.]\+$' ; then
- echo -n $RESULT
- return
- fi
-
- sleep $delay
- delay=$((delay>>1))
- done
- # Change 'stop' to something else to let the script running
- echo -n "stop" # stop: the script will terminate
-
-}
-
-# Infinite loop; checks, whether the IP of FQDN has changed.
-# If so, the affected connection gets restarted.
-#
-RESTART_COUNT=0
-REMOTE_IP_OLD=$(get_ip $2)
-log "start watching $REMOTE_IP_OLD"
-
-while [ $REMOTE_IP_OLD != 'stop' ] ; do
- sleep $CHECK_INTERVAL
- # Skip check until IPSec is running. Update IP_OLD while our ipsec is down
- /usr/sbin/ipsec auto --status >/dev/null 2>&1 || {
- REMOTE_IP_OLD=$(get_ip $2)
- continue
- }
-
- REMOTE_IP_NEW=$(get_ip $2)
-
- if test "${REMOTE_IP_OLD}" != "${REMOTE_IP_NEW}"; then
- /usr/sbin/ipsec auto --down $1
- /usr/sbin/ipsec auto --replace $1
- /usr/sbin/ipsec auto --rereadsecrets
- /usr/sbin/ipsec auto --up $1
- let RESTART_COUNT++
- log "Remote IP has changed from $REMOTE_IP_OLD to $REMOTE_IP_NEW. Connection restarted (#$RESTART_COUNT times)."
- REMOTE_IP_OLD=$REMOTE_IP_NEW
- fi
-done
+#!/bin/sh
+##################################################
+##### VPN-Watch.sh Version 1.6.3 #####
+##################################################
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# Written by: Daniel Berlin <daniel.berlin@itechnology.de>.
+# Download: http://www.itechnology.de/front_content.php?idcat=87
+#
+
+# changed by: Rüdiger Sobeck
+# last changed: 31-01-2006
+
+# Configuration
+#
+CHECK_INTERVAL='120' # Check this often (in seconds)
+DNS_RESOLVE_TRIES='3' # Try to resolve IPs this often (each try takes max. 2 seconds)
+NICENESS='+5' # Adjust niceness of child processes: '-20' ... '+19'; '0' is default
+ipfire_VPN_CONFIG='/var/ipfire/vpn/config' # Location of ipfire's vpn configuration file
+ipfire_VPN_SETTINGS='/var/ipfire/vpn/settings' # Location of ipfire's vpn settings file
+VERSION='1.6.3'
+
+# Workaround for nonexistent "nl" command on ipfire 1.4.x
+nl --help >/dev/null 2>&1
+if test $? -ne 0; then
+ alias nl='cat'
+fi
+
+MyHost=`grep VPN_IP /var/ipfire/vpn/settings | cut --delimiter='=' --output-delimiter=' ' -f2`
+MyIP=`cat /var/ipfire/red/local-ipaddress`
+MyDynDnsIP=`ping -c 1 "$1" 2>/dev/null | head -n1 | awk '{print $3}' | tr -d '()' | tr -d ':'`
+
+case "$1" in
+ 'start' | '--start')
+ if test ! -r "$ipfire_VPN_CONFIG"; then
+ echo 'Error: cannot read ipfire VPN configuration file; exit.' >&2
+ exit 1
+ fi
+
+ mknod -m 0660 "/var/run/$(basename $0)" p >/dev/null 2>&1 # Create pipe for status-information
+
+ # Read VPN configuration and fork a child process for each VPN connection
+ #
+ while read line; do
+ VPN=($(echo $line | cut --delimiter=',' --output-delimiter=' ' -f1,2,3,5,6,12)) #
+ CONNR=${VPN[0]} # connection number
+ CONACTIVE=${VPN[1]} # active (on|off)
+ CONNAME=${VPN[2]} # connection name
+ CONTYPE=${VPN[3]} # connection type (host|net)
+ CONCERTPSK=${VPN[4]} # key type (cert|psk)
+ CONDNSNAME=${VPN[5]} # FQDN name of other side
+
+ echo -n "${CONACTIVE}" | grep -qi '^off$' && continue # Ignore: deactivated connections
+ echo -n "${CONTYPE}" | grep -qi '^host$' && continue # Ignore: Roadwarriors (->DPD)
+# echo -n "${VPN[1]}${MyHost}" | grep -q '^[[:digit:]\.]\+$' && continue # Ignore: "left" and "right" side set to an IP
+
+ $0 'conn:' "${CONNAME}" "${MyHost}" "${CONDNSNAME}" "${CONNR}" >/dev/null 2>&1 & # Fork child process (parameters: "conn: NAME LEFT RIGHT NUMBER")
+ echo -n 'S'
+ done < "$ipfire_VPN_CONFIG"
+ echo Â"ÂStarte VPN-Watch"
+ exit 0 # Parent dies here... RIP
+ ;;
+ 'stop' | '--stop')
+ # Terminate processes
+ for proc in $(pidof -x -o %PPID $(basename $0)); do
+ kill -15 $proc
+ echo -n 'T'
+ done
+ sleep 1
+ # Kill remaining processes
+ for proc in $(pidof -x -o %PPID $(basename $0)); do
+ kill -9 $proc
+ echo -n 'K'
+ done
+ rm -f "/var/run/$(basename $0)" # Remove pipe
+ echo "Stoppe VPN-Watch"
+ exit 0
+ ;;
+ 'restart' | '--restart')
+ $0 stop
+ $0 start
+ exit 0
+ ;;
+ 'status' | '--status')
+ echo "VPN-Watch ${VERSION} (mail: daniel@itechnology.de, web: www.itechnology.de/vpn-watch)"
+ if ps --no-heading axw | grep -v 'grep' | grep -q "$(basename $0) conn: "; then
+ trap '' USR1
+ killall -q -g -s USR1 -- $(basename $0)
+ sleep 1
+ cat "/var/run/$(basename $0)" | sort | nl # Read children's info from pipe
+ else
+ echo ' no instances running.'
+ fi
+ exit 0
+ ;;
+ 'conn:')
+ # Children proceed here...
+ renice ${NICENESS:-0} -p $$ >/dev/null 2>&1 # Adjust niceness
+ shift # Remove the first positional parameter ("conn:"), as we don't need it anymore
+ ;;
+ *)
+ echo "Usage: $0 { start | stop | restart | status }" >&2
+ exit 1
+ ;;
+esac
+
+# Logging, signal handlers
+#
+alias log="logger -t '$(basename $0 | cut -d '.' -f 1) ${VERSION}' \(${1}\)"
+trap 'log "terminated after ${RESTART_COUNT} restarts."' EXIT
+trap 'echo "connection \"${1}\" restarted ${RESTART_COUNT} times" >>/var/run/$(basename $0)' USR1
+
+log "started"
+
+# Get IP of a FQDN... using 'arp', 'traceroute' or 'ping',
+# because ipfire has no 'nslookup', 'host' or 'dig' command.
+#
+function get_ip () {
+ local RESULT=''
+ for ((i=1; ${i} <= ${DNS_RESOLVE_TRIES}; i++)); do
+ if which arp >/dev/null 2>&1; then
+ RESULT=$(arp "$1" 2>/dev/null | awk '{ print $2 }' | tr -d '()')
+ elif which traceroute >/dev/null 2>&1; then
+ RESULT=$(traceroute -m1 -q1 "$1" 2>/dev/null | head -n1 | awk '{ print $4 }' | tr -d '(),')
+ else
+ RESULT=$(ping -c 1 "$1" 2>/dev/null | head -n1 | awk '{print $3}' | tr -d '()' | tr -d ':')
+ fi
+ test -n "$RESULT" && break
+ done
+ test -z "$RESULT" && log "Warning: could not resolve ${1} after ${DNS_RESOLVE_TRIES} tries..."
+ echo -n "$RESULT"
+}
+
+function get_tunnelip () {
+ file=/var/tmp/$1.remoteip
+ local TRESULT=''
+ TVPN=`grep "$1" /var/ipfire/vpn/config| awk 'BEGIN{FS=","}{print $2}'`
+ DYNHOST=`grep "$1" /var/ipfire/vpn/config| awk 'BEGIN{FS=","}{print $12}'`
+ CONNR=`grep "$1" /var/ipfire/vpn/config| awk 'BEGIN{FS=","}{print $1}'`
+ REMOTEIP=`/usr/bin/ping -c 1 "$DYNHOST" 2>/dev/null | head -n1 | awk '{print $3}' | tr -d '()' | tr -d ':'`
+ if ! test -f $file; then
+ cat $REMOTEIP > $file
+ fi
+ OLDIP=`cat $file`
+ TUNIP=`ipsec whack --status | grep "$1"`
+ if [ "$TUNIP" != "" ]; then
+ TUNIP=`ipsec whack --status | grep "$1" | awk 'BEGIN{FS="["}{print $2}' | awk 'BEGIN{FS="---"}{print $3}'`
+ log "currently used tunnel IP = $TUNIP, current remote IP = $REMOTEIP"
+ echo $REMOTEIP > $file
+ TRESULT=${TUNIP}
+ fi
+
+ test -n "$TRESULT" && break
+ test -z "$TRESULT" && log "Warning: could not retrieve last used VPN tunnel IP..."
+ echo -n "$TRESULT"
+}
+
+# Restarts a VPN connection
+#
+function restart_vpn () {
+ if test -x /usr/local/bin/ipsecctrl; then
+ /usr/local/bin/ipsecctrl D "$1" # This works for ipfire 1.4.x
+ /usr/local/bin/ipsecctrl R # re-read secrets
+ /usr/local/bin/ipsecctrl S "$1" # start tunnel
+ else
+ ipsec auto --down "$1" # This works for ipfire 1.3.x
+ ipsec auto --unroute "$1"
+ ipsec auto --delete "$1"
+ ipsec auto --rereadall
+ ipsec auto --add "$1"
+ ipsec auto --route "$1"
+ ipsec auto --up "$1"
+ fi
+}
+
+# Get left and right IP
+#
+LEFT_IP_OLD=$MyIP
+RIGHT_IP_OLD=$(get_ip $3)
+
+# Infinite loop; checks, whether the IP of a left or right FQDN has changed.
+# If so, the affected connection gets restarted; this is logged to syslog.
+#
+RESTART_COUNT=0
+while :; do
+ sleep $CHECK_INTERVAL
+
+ # Skip check until IPSec is running
+ ipsec auto --status >/dev/null 2>&1 || continue
+
+ # get own IP (may have changed)
+ ThisHostIP=`cat /var/ipfire/red/local-ipaddress`
+
+ # this our own IP as reported in /var/ipfire/ppp/local-ipadress
+ LEFT_IP_NEW=$ThisHostIP
+ # check our own DYNDNS IP
+ LEFT_IP_DYN=$(get_ip $MyHost)
+ # this is DYNDNS IP of other side
+ RIGHT_IP_NEW=$(get_ip $3)
+ # this the last used (right) IP for VPN-Tunnel
+ RIGHT_TUN_IP_OLD=$(get_tunnelip $1)
+
+# for whatever reason, ipsec did not notice our own IP has changed for this connection
+ if [ "${LEFT_IP_NEW}" != "${LEFT_IP_DYN}" ]; then
+ restart_vpn "$4"
+ let RESTART_COUNT++
+ log "Red IP = $LEFT_IP_NEW, IP by DynDNS = $LEFT_IP_DYN"
+ log 'incorrect dynamic IP in tunnel used: restarting connection...'
+ fi
+
+# left or right IP has changed...
+ if test "${LEFT_IP_OLD} ${RIGHT_IP_OLD}" != "${LEFT_IP_NEW} ${RIGHT_IP_NEW}"; then
+ restart_vpn "$4"
+ let RESTART_COUNT++
+ log 'left or right IP has changed: restarting connection...'
+ fi
+
+# right IP / IP of tunnel endpoint has changed...
+ if [ "$RIGHT_TUN_IP_OLD" != "" ]; then
+ if test "${RIGHT_TUN_IP_OLD}" != "${RIGHT_IP_NEW}"; then
+ restart_vpn "$4"
+ let RESTART_COUNT++
+ log 'VPN tunnel IP has changed: restarting connection...'
+ fi
+ fi
+
+ LEFT_IP_OLD=$LEFT_IP_NEW
+ RIGHT_IP_OLD=$RIGHT_IP_NEW
+done
+