vpn-watch: restart ipsec only if ip's has changed.
[people/pmueller/ipfire-2.x.git] / src / scripts / vpn-watch
index 22107d0..3f7757a 100755 (executable)
-#!/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
-
+#!/usr/bin/perl 
+##################################################
+#####     VPN-Watch.pl     Version 0.4c      #####
+##################################################
+#                                                #
+#   VPN-Watch is part of the IPFire Firewall     #
+#                                                #
+##################################################
+
+use strict;
+
+require '/var/ipfire/general-functions.pl';
+my @vpnsettings;
+my $i = 0;
+my $file = "/var/run/vpn-watch.pid";
+my $debug = 0;
+
+if ( -e $file ){
+  logger("There my be another vpn-watch runnning because $file exists, vpn-watch will try kill the process.");
+  open(FILE, "<$file");
+    my $PID = <FILE>;
+    close(FILE);
+    system("kill -9 $PID");
+  }
+
+system("echo $$ > $file");
+    
+while ( $i == 0){
+  if ($debug){logger("We will wait 60 seconds before next action.");}
+    sleep(60);
+  
+  if (open(FILE, "<${General::swroot}/vpn/config")) {
+    @vpnsettings = <FILE>;
+    close(FILE);
+    unless(@vpnsettings) {exit 1;}
+  }
+
+my $status = `ipsec whack --status`;
+foreach (@vpnsettings){
+ my @settings = split(/,/,$_);
+
+  if ($settings[27] ne 'RED'){next;}
+  if ($settings[4] ne 'net'){next;}  
+  if ($settings[1] ne 'on'){next;}chomp($settings[29]);
+  if ($settings[29] ne 'on'){next;}
+  my $remotehostname = $settings[11];
+  
+  if ($debug){logger("Checking connection to $remotehostname.");}
+  
+  my $remoteip = `/usr/bin/ping -c 1 $remotehostname 2>/dev/null | head -n1 | awk '{print \$3}' | tr -d '()' | tr -d ':'`;chomp($remoteip);
+  if ($remoteip eq ""){next;if ($debug){logger("Unable to resolve $remotehostname.");}}
+  my $ipmatch= `echo "$status" | grep $remoteip | grep $settings[2]`;
+  
+  if ( $ipmatch eq '' ){
+    logger("Remote IP for host $remotehostname-$remoteip has changed, restarting ipsec.");
+    system("/usr/local/bin/ipsecctrl S");
+    last; #all connections will reloaded
+  }
+ }
+ if ($debug){logger("All connections may be fine nothing was done.");}
 }
 
-# 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)
+sub logger {
+        my $log = shift;
+        system("logger -t vpnwatch \"$log\"");
+}
 
-       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