]>
Commit | Line | Data |
---|---|---|
bf7c473f MT |
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 | ||
a08c3a2e | 45 | if /usr/bin/test -p /var/run/$(basename $0); then |
bf7c473f MT |
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 |