Netzwerkscripts erweitert.
[people/pmueller/ipfire-2.x.git] / src / scripts / vpn-watch
1 #!/bin/sh
2 #
3 # IPFire script - vpn-watch
4 #
5 # This code is distributed under the terms of the GPL
6 #
7 # (c) Daniel Berlin <daniel berlin_itechnology de> - Check for
8 # remote peer with dynamic IPs and restart when change
9 # is detected. Works with DPD which is not perfect!
10 #
11 # 2006: Franck - adapted original script to fit in IPCop 1.4
12 # 2007: Michael Tremer - mitch@ipfire.org - Merged into IPFire
13 #
14 # This program is free software; you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation; either version 2, or (at your option)
17 # any later version.
18 #
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
23 #
24
25 #
26 # Configuration
27 #
28
29 VPN_CONFIG='/var/ipfire/vpn/config' # Location of IPFire's vpn configuration file
30 SETTINGS='/var/ipfire/vpn/settings' # and settings
31
32 CHECK_INTERVAL='60' # Check this often (in seconds)
33 DNS_RESOLVE_TRIES='4' # Try to resolve IPs this often (each try takes max. 2 seconds)
34 NICENESS='+5' # Adjust niceness of child processes: '-20' ... '+19'; '0' is default
35 case "$1" in
36 'start' | '--start')
37 eval $(/usr/local/bin/readhash $SETTINGS)
38 test "${VPN_WATCH}" != "on" && exit 1 # not activated, cannot start!
39
40 if test ! -r "$VPN_CONFIG"; then
41 echo 'Error: cannot read IPFire VPN configuration file; exit.' >&2
42 exit 1
43 fi
44
45 if /bin/test -p /var/run/$(basename $0); then
46 if ps --no-heading axw | grep -v 'grep' | grep -q "$(basename $0) conn: "; then
47 echo "Error: use '$(basename $0) stop' please; exit." >&2
48 exit 1
49 else
50 rm /var/run/$(basename $0) # pipe was left alone, correct error condition
51 fi
52 fi
53
54 # the pipe serves for "-status" but is not used yet
55 /bin/mknod -m 0660 "/var/run/$(basename $0)" p >/dev/null 2>&1 # Create pipe for status-information
56
57 #
58 # Read VPN configuration and fork a child process for each VPN connection active, net-to-net & RED
59 #
60 while read line; do
61 VPN=($(echo $line | cut --delimiter=',' --output-delimiter=' ' -f2,3,5,12,28 )) # Activated, Name, Host/Net-to-net, Remote, ITF.
62 test "${VPN[0]}" != "on" && continue # Ignore: deactivated connections
63 test "${VPN[2]}" = "host" && continue # Ignore: roadwarriors
64 ## test "${VPN[4]}" != "RED" && continue # Ignore: local vpns needed or not ?
65 echo -n "${VPN[3]}" | grep -q '^[[:digit:]\.]\+$' && continue #If fixed remote IP, no need to watch!
66 $0 'conn:' "${VPN[1]}" "${VPN[3]}">/dev/null 2>&1 & #Fork child process (parameters: "conn: NAME RIGHT")
67 done < "$VPN_CONFIG"
68 exit 0 # Parent dies here... RIP
69 ;;
70
71 'stop' | '--stop')
72 # Terminate processes
73 for proc in $(pidof -x -o %PPID $(basename $0)); do
74 kill -s SIGTERM -- "$proc"
75 done
76 sleep 1
77
78 # Kill remaining processes
79 for proc in $(/bin/pidof -x -o %PPID $(basename $0)); do
80 kill -s SIGKILL -- "$proc"
81 done
82 rm -f "/var/run/$(basename $0)" # Remove pipe
83 exit 0
84 ;;
85
86 #'status' | '--status')
87 # echo "VPN-Watch"
88 # if ps --no-heading axw | grep -v 'grep' | grep -q "$(basename $0) conn: "; then
89 # trap '' USR1
90 # killall -q -g -s USR1 -- $(basename $0)
91 # sleep 1
92 # cat "/var/run/$(basename $0)" | sort # Read children's info from pipe
93 # else
94 # echo ' no instance running.'
95 # fi
96 # exit 0
97 # ;;
98
99 'conn:')
100 # Children proceed here...
101 renice ${NICENESS:-0} -p $$ >/dev/null 2>&1 # Adjust niceness
102 shift # Remove the first positional parameter ("conn:"), as we don't need it anymore
103 ;;
104 *)
105 /bin/echo "Usage: $0 { start | stop }" >&2
106 exit 1
107 ;;
108 esac
109
110 # Logging, signal handlers
111 alias log="logger -t vpn-watch \'${1}\':"
112
113 trap 'log "terminated after ${RESTART_COUNT} restarts."' EXIT
114 #trap 'echo "connection \"${1}\" restarted ${RESTART_COUNT} times" >>/var/run/$(basename $0)' USR1
115
116 #
117 # Get IP of a FQDN... using 'host' command. Everything is ok when dns server responds.
118 # If no response,
119 # -maybe RED is down. The script can terminate. It will restart with rc.updatered.
120 # or
121 # -the dns server is down. In this case, terminate the script is not a good idea...
122 # Thus 4 retries before returning response 'stop'
123 #
124 function get_ip () {
125 local RESULT=''
126 # delay divided by two for each loop
127 delay=8
128 for ((i=1; ${i} <= ${DNS_RESOLVE_TRIES}; i++)); do
129
130 # extract IP address
131 RESULT=$(/usr/bin/host "$1" 2>/dev/null| awk '{ print $4 }')
132 if echo -n $RESULT | /bin/grep -q '^[[:digit:]\.]\+$' ; then
133 echo -n $RESULT
134 return
135 fi
136
137 sleep $delay
138 delay=$((delay>>1))
139 done
140 # Change 'stop' to something else to let the script running
141 echo -n "stop" # stop: the script will terminate
142
143 }
144
145 # Infinite loop; checks, whether the IP of FQDN has changed.
146 # If so, the affected connection gets restarted.
147 #
148 RESTART_COUNT=0
149 REMOTE_IP_OLD=$(get_ip $2)
150 log "start watching $REMOTE_IP_OLD"
151
152 while [ $REMOTE_IP_OLD != 'stop' ] ; do
153 sleep $CHECK_INTERVAL
154 # Skip check until IPSec is running. Update IP_OLD while our ipsec is down
155 /usr/sbin/ipsec auto --status >/dev/null 2>&1 || {
156 REMOTE_IP_OLD=$(get_ip $2)
157 continue
158 }
159
160 REMOTE_IP_NEW=$(get_ip $2)
161
162 if test "${REMOTE_IP_OLD}" != "${REMOTE_IP_NEW}"; then
163 /usr/sbin/ipsec auto --down $1
164 /usr/sbin/ipsec auto --replace $1
165 /usr/sbin/ipsec auto --rereadsecrets
166 /usr/sbin/ipsec auto --up $1
167 let RESTART_COUNT++
168 log "Remote IP has changed from $REMOTE_IP_OLD to $REMOTE_IP_NEW. Connection restarted (#$RESTART_COUNT times)."
169 REMOTE_IP_OLD=$REMOTE_IP_NEW
170 fi
171 done