New function: xalloc_and_zero()
[tinc] / redhat / tinc
1 #!/bin/sh
2 #
3 # tinc          tincd VPN setup script
4 #
5 # chkconfig:    2345 46 54
6 #
7 # version:      1.0.8
8 # authors:      Lubomir Bulej <pallas@kadan.cz>
9 #               Mads Kiilerich <mads@kiilerich.com>
10 #
11 # description:  This script parses tinc configuration files for networks given \
12 #               in /etc/tinc/nets.boot and for each of the networks it sets up \
13 #               the interface and static routes and starts the tinc daemon.
14 #
15 # processname:  tincd
16
17 # Source function library.
18 . /etc/rc.d/init.d/functions
19
20 # Source networking configuration.
21 . /etc/sysconfig/network
22
23 # Check that networking is up.
24 [ ${NETWORKING} = "no" ] && exit 0
25
26 #############################################################################
27 # configuration & sanity checks
28
29 TINCD=/usr/sbin/tincd
30 TCONF=/etc/tinc
31 TPIDS=/var/run
32 #DEBUG=-dddd
33
34 NETSFILE=$TCONF/nets.boot
35
36 # Check the daemon
37 if [ ! -x $TINCD ]; then
38     echo "**tinc: $TINCD does not exist or is not executable!" >&2
39     exit
40 fi
41
42 # Check if ip-route is installed
43 if [ ! -f /sbin/ip ]; then
44     echo "**tinc: ip-route utilities not installed!" >&2
45     exit
46 fi
47
48 # Check the kernel
49 if ! ip addr &> /dev/null; then
50     echo "**tinc: kernel not configured for use with ip-route!" >&2
51     exit
52 fi
53
54 # Check the configuration directory
55 if [ ! -d $TCONF ]; then
56     echo "**tinc: configuration directory ($TCONF) not found!" >&2
57     exit
58 fi
59
60 # Check nets.boot
61 if [ ! -f $NETSFILE ]; then
62     echo "**tinc: file with list of VPNs to start ($NETSFILE) not found!" >&2
63     exit
64 fi
65
66 # Load names of networks to be started
67 NETS="$(sed -e 's/#.*//; s/[[:space:]]//g; /^$/ d' $NETSFILE)"
68
69
70 ##############################################################################
71 # prefix_to_mask        Converts prefix length to netmask
72 #                       eg. 17 -> 255.255.128.0
73 # $1 ... prefix
74
75 prefix_to_mask () {
76     _MSK=""; _len="$1"
77     for _dot in "." "." "." " "; do
78         if [ ${_len} -ge 8 ]; then 
79            _fld=8
80         else           
81            _fld="${_len}"
82         fi
83         
84         _MSK="${_MSK}$((255 & (255 << (8 - _fld))))${_dot}"
85         _len=$((_len - _fld))
86     done
87     
88     echo ${_MSK}
89 }
90
91
92 ##############################################################################
93 # mask_to_prefix        Converts netmask to prefix length
94 #                       eg. 255.255.192.0 -> 18         
95 # $1 ... netmask
96
97 mask_to_prefix () {
98     _LEN=0; _msk="$1"
99     for _tmp in 1 2 3 4; do
100         _fld=${_msk%%.*}
101         _msk=${_msk#*.}
102         
103         while [ ${_fld} -ne 0 ]; do
104             _fld=$(((_fld << 1) & 255))
105             _LEN=$((_LEN + 1))
106         done
107     done
108     
109     echo ${_LEN}
110 }
111
112
113 ##############################################################################
114 # vpn_load ()           Loads VPN configuration
115
116 # $1 ... VPN to load
117
118 vpn_load () {
119     CFG="$TCONF/$1/tinc.conf"
120     [ -f $CFG ] || { MSG="$CFG does not exist!"; return 1; }
121     
122     # load TINCD config
123     DEV="$(grep -i -e '^[[:space:]]*TapDevice' $CFG | sed 's/[[:space:]]//g; s/^.*=//g')"
124     VPN="$(grep -i -e '^[[:space:]]*(MyOwnVPNIP|MyVirtualIP)' -E $CFG | sed 's/[[:space:]]//g; s/^.*=//g')"
125     IFM="$(grep -i -e '^[[:space:]]*VPNMask' $CFG | sed 's/[[:space:]]//g; s/^.*=//g')"
126     
127     # TapDevice syntax validation
128     [ -z "$DEV" ] && \
129         { MSG="TapDevice required!"; return 1; }
130     [ $(echo $DEV | wc -l) -gt 1 ] && \
131         { MSG="multiple TapDevice entries not allowed!"; return 1; }
132     echo $DEV | grep -q -x -E '/dev/tap[[:digit:]]+' ||
133         { MSG="TapDevice should be in form /dev/tapX!"; return 1; }
134
135     # MyOwnVPNIP/MyVirtualIP syntax validation  
136     [ -z "$VPN" ] && \
137         { MSG="MyOwnVPNIP/MyVirtualIP required!"; return 1; }
138     [ $(echo $VPN | wc -l) -gt 1 ] && \
139         { MSG="multiple MyOwnVPNIP/MyVirtualIP entries not allowed!"; return 1; }
140     echo $VPN | grep -q -x -E \
141         '([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}/[[:digit:]]{1,2}' || \
142         { MSG="badly formed MyOwnVPNIP/MyVirtualIP address $VPN!"; return 1; }
143
144     # VPNMask syntax validation 
145     [ $(echo $IFM | wc -l) -gt 1 ] && \
146         { MSG="multiple VPNMask entries not allowed!"; return 1; }
147         
148                                                                                             
149     # device & IP address extraction
150     TAP=${DEV##*/}
151     NUM=${TAP#tap}
152     ADR=${VPN%%/*}
153                                                                                                             
154     # netmask is calculated from MyVirtualIP netmask prefix length, except when
155     # VPNMask is specified, in which case it is used instead of default prefix
156
157     # VPNMask not specified
158     if [ -z "$IFM" ]; then
159         LEN=${VPN##*/}
160         MSK=$(prefix_to_mask $LEN)
161         
162     # VPNMask is prefix length, convert it to netmask for MSK
163     elif echo $IFM | grep -q -x -E '[[:digit:]]{1,2}'; then
164         VPN="$ADR/$IFM"
165         MSK=$(prefix_to_mask $IFM)
166         
167     # VPNMask is netmask, convert it to prefix length for VPN
168     elif echo $IFM | grep -q -x -E '([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}'; then
169         VPN="$ADR/$(mask_to_prefix $IFM)"
170         MSK="$IFM"
171         
172     else
173         MSG="badly formed interface netmask (VPNMask=$IFM)!"
174         return 1
175     fi  
176     
177
178     # Network & broadcast addresses
179     BRD=$(ipcalc --broadcast $ADR $MSK | cut -d"=" -f2)
180     NET=$(ipcalc --network $ADR $MSK | cut -d"=" -f2)
181
182     # MAC address
183     MAC=$(printf "fe:fd:%0.2x:%0.2x:%0.2x:%0.2x" $(echo $ADR | { IFS=. ; read a b c d; echo $a $b $c $d; }))
184     
185     # debugging 
186     # echo >&2
187     # echo "VPN $VPN TAP $TAP NUM $NUM MAC $MAC IFM $IFM" >&2
188     # echo "ADR $ADR MSK $MSK NET $NET BRD $BRD" >&2
189     
190     return 0
191 }
192
193
194 ##############################################################################
195 # vpn_start ()          starts specified VPN
196
197 # $1 ... VPN to start
198
199 vpn_start () {    
200     MSG=""; ERR=""
201     vpn_load $1 || return 1
202             
203     # create device file
204     if [ ! -c $DEV ]; then
205         [ -e $DEV ] && rm -f $DEV
206         mknod --mode=0600 $DEV c 36 $((16 + NUM))
207     fi
208     
209     # load device module
210     ERR="$(insmod ethertap -o "ethertap$NUM" unit="$NUM" 2>&1 1> /dev/null)" ||
211         { MSG="could not insmod ethertap as unit $NUM!"; return 2; }
212     
213     # configure the interface
214     ERR="$(ip link set $TAP address $MAC 2>&1)" ||
215         { MSG="could not set address for device $TAP!"; return 3; }
216         
217     ERR="$(ip link set $TAP up 2>&1)" ||
218         { MSG="could not bring up device $TAP!"; return 3; }
219         
220     ERR="$(ip addr add $VPN brd $BRD dev $TAP 2>&1)" ||
221         { MSG="could not set IP address for device $TAP!"; return 3; }
222     
223     # start tincd
224     $TINCD --net="$1" $DEBUG || \
225         { MSG="could not start daemon for network $1"; return 3; }
226
227     # setup custom static routes
228     /etc/sysconfig/network-scripts/ifup-routes $TAP
229
230     return 0
231 } # vpn_start
232
233
234 ##############################################################################
235 # vpn_stop ()           Stops specified VPN
236 #
237 # $1 ... VPN to stop
238
239 vpn_stop () {
240     MSG=""; ERR=""
241     vpn_load $1 || return 1
242     
243     # kill the tincd daemon
244     PID="$TPIDS/tinc.$1.pid"
245     if [ -f $PID ]; then
246         $TINCD --net="$1" --kill &> /dev/null
247         RET=$?
248     
249         if [ $RET -eq 0 ]; then
250             dly=0
251             while [ $dly -le 5 ]; do
252                 [ -f $PID ] || break
253                 sleep 1; dly=$((dly + 1))
254             done
255         fi
256         
257         # remove stale PID file
258         [ -f $PID ] && rm -f $PID
259     fi
260     
261     # bring the interface down
262     ip addr flush dev $TAP &> /dev/null
263     ip link set $TAP down &> /dev/null
264     
265     # remove ethertap module
266     rmmod "ethertap$NUM" &> /dev/null
267     
268     return 0
269 } # vpn_stop
270
271
272 # Check if there is anything to start
273 if [ ! -z "$1" -a "$1" != "status" -a -z "$NETS" ]; then
274     echo "**tinc: no networks found in $NETSFILE!" >&2
275     exit
276 fi
277
278
279 # See how we were called.
280 case "$1" in
281     start)
282         for vpn in $NETS; do
283             echo -n "Bringing up TINC network $vpn: "
284             vpn_start $vpn && \
285                 success "startup of network $vpn" || \
286                 failure "startup of network $vpn"
287             echo
288             
289             if [ ! -z "$MSG" ]; then
290                 [ ! -z "$ERR" ] && echo "$ERR" >&2
291                 echo "**tinc: $MSG" >&2
292             fi
293         done
294         
295         touch /var/lock/subsys/tinc
296         ;;
297         
298     stop)
299         for vpn in $NETS; do
300             echo -n "Shutting down TINC network $vpn: "
301             vpn_stop $vpn && \
302                 success "shutdown of network $vpn" || \
303                 failure "shutdown of network $vpn"
304             echo
305             
306             if [ ! -z "$MSG" ]; then
307                 [ ! -z "$ERR" ] && echo "$ERR" >&2
308                 echo "**tinc: $MSG" >&2
309             fi
310         done
311         
312         rm -f /var/lock/subsys/tinc
313         ;;
314         
315     status)
316         echo -n "Configured VPNs: "
317         for vpn in $NETS; do
318             PID="$TPIDS/tinc.$vpn.pid"
319             
320             [ -f $PID ] && PID="$(cat $PID)" || PID="-dead-"
321             ps ax | grep "^[[:space:]]*$PID" && STS="OK" || STS="DEAD"
322             echo -n "$vpn:$STS "
323         done
324         echo             
325         ;;      
326         
327     restart)
328         $0 stop
329         $0 start
330         ;;
331                 
332     *)
333         echo "Usage: tinc {start|stop|status|restart}"
334         exit 1
335 esac
336
337 exit 0