1#!@SHELL@
2#
3# run OpenVPN client against ``test reference'' server
4# - check that ping, http, ... via tunnel works
5# - check that interface config / routes are properly cleaned after test end
6#
7# prerequisites:
8# - openvpn binary in current directory
9# - writable current directory to create subdir for logs
10# - t_client.rc in current directory OR source dir that specifies tests
11# - for "ping4" checks: fping binary in $PATH
12# - for "ping6" checks: fping6 binary in $PATH
13#
14
15srcdir="${srcdir:-.}"
16top_builddir="${top_builddir:-..}"
17if [ -r "${top_builddir}"/t_client.rc ] ; then
18    . "${top_builddir}"/t_client.rc
19elif [ -r "${srcdir}"/t_client.rc ] ; then
20    . "${srcdir}"/t_client.rc
21else
22    echo "$0: cannot find 't_client.rc' in build dir ('${top_builddir}')" >&2
23    echo "$0: or source directory ('${srcdir}'). SKIPPING TEST." >&2
24    exit 77
25fi
26
27if [ ! -x "${top_builddir}/src/openvpn/openvpn" ]
28then
29    echo "no (executable) openvpn binary in current build tree. FAIL." >&2
30    exit 1
31fi
32
33if [ ! -w . ]
34then
35    echo "current directory is not writable (required for logging). FAIL." >&2
36    exit 1
37fi
38
39if [ -z "$CA_CERT" ] ; then
40    echo "CA_CERT not defined in 't_client.rc'. SKIP test." >&2
41    exit 77
42fi
43
44if [ -z "$TEST_RUN_LIST" ] ; then
45    echo "TEST_RUN_LIST empty, no tests defined.  SKIP test." >&2
46    exit 77
47fi
48
49# make sure we have permissions to run ifconfig/route from OpenVPN
50# can't use "id -u" here - doesn't work on Solaris
51ID=`id`
52if expr "$ID" : "uid=0" >/dev/null
53then :
54else
55    if [ -z "$RUN_SUDO" ]
56    then
57        echo "$0: this test must run be as root, or RUN_SUDO=... " >&2
58        echo "      must be set correctly in 't_client.rc'. SKIP." >&2
59        exit 77
60    fi
61fi
62
63LOGDIR=t_client-`hostname`-`date +%Y%m%d-%H%M%S`
64if mkdir $LOGDIR
65then :
66else
67    echo "can't create log directory '$LOGDIR'. FAIL." >&2
68    exit 1
69fi
70
71exit_code=0
72
73# ----------------------------------------------------------
74# helper functions
75# ----------------------------------------------------------
76# print failure message, increase FAIL counter
77fail()
78{
79    echo ""
80    echo "FAIL: $@" >&2
81    fail_count=$(( $fail_count + 1 ))
82}
83
84# print "all interface IP addresses" + "all routes"
85# this is higly system dependent...
86get_ifconfig_route()
87{
88    # linux / iproute2? (-> if configure got a path)
89    if [ -n "@IPROUTE@" ]
90    then
91	echo "-- linux iproute2 --"
92	@IPROUTE@ addr show     | grep -v valid_lft
93	@IPROUTE@ route show
94	@IPROUTE@ -o -6 route show | grep -v ' cache' | sed -e 's/expires [0-9]*sec//'
95	return
96    fi
97
98    # try uname
99    case `uname -s` in
100	Linux)
101	   echo "-- linux / ifconfig --"
102	   LANG=C @IFCONFIG@ -a |egrep  "( addr:|encap:)"
103	   LANG=C @NETSTAT@ -rn -4 -6
104	   return
105	   ;;
106	FreeBSD|NetBSD|Darwin)
107	   echo "-- FreeBSD/NetBSD/Darwin [MacOS X] --"
108	   @IFCONFIG@ -a | egrep "(flags=|inet)"
109	   @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$NF }'
110	   return
111	   ;;
112	OpenBSD)
113	   echo "-- OpenBSD --"
114	   @IFCONFIG@ -a | egrep "(flags=|inet)" | \
115		sed -e 's/pltime [0-9]*//' -e 's/vltime [0-9]*//'
116	   @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$NF }'
117	   return
118	   ;;
119	SunOS)
120	   echo "-- Solaris --"
121	   @IFCONFIG@ -a | egrep "(flags=|inet)"
122	   @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$6 }'
123	   return
124	   ;;
125    esac
126
127    echo "get_ifconfig_route(): no idea how to get info on your OS.  FAIL." >&2
128    exit 20
129}
130
131# ----------------------------------------------------------
132# check ifconfig
133#  arg1: "4" or "6" -> for message
134#  arg2: IPv4/IPv6 address that must show up in out of "get_ifconfig_route"
135check_ifconfig()
136{
137    proto=$1 ; shift
138    expect_list="$@"
139
140    if [ -z "$expect_list" ] ; then return ; fi
141
142    for expect in $expect_list
143    do
144	if get_ifconfig_route | fgrep "$expect" >/dev/null
145	then :
146	else
147	    fail "check_ifconfig(): expected IPv$proto address '$expect' not found in ifconfig output."
148	fi
149    done
150}
151
152# ----------------------------------------------------------
153# run pings
154#  arg1: "4" or "6" -> fping/fing6
155#  arg2: "want_ok" or "want_fail" (expected ping result)
156#  arg3... -> fping arguments (host list)
157run_ping_tests()
158{
159    proto=$1 ; want=$2 ; shift ; shift
160    targetlist="$@"
161
162    # "no targets" is fine
163    if [ -z "$targetlist" ] ; then return ; fi
164
165    case $proto in
166	4) cmd=fping ;;
167	6) cmd=fping6 ;;
168	*) echo "internal error in run_ping_tests arg 1: '$proto'" >&2
169	   exit 1 ;;
170    esac
171
172    case $want in
173	want_ok)   sizes_list="64 1440 3000" ;;
174	want_fail) sizes_list="64" ;;
175    esac
176
177    for bytes in $sizes_list
178    do
179	echo "run IPv$proto ping tests ($want), $bytes byte packets..."
180
181	echo "$cmd -b $bytes -C 20 -p 250 -q $targetlist" >>$LOGDIR/$SUF:fping.out
182	$cmd -b $bytes -C 20 -p 250 -q $targetlist >>$LOGDIR/$SUF:fping.out 2>&1
183
184	# while OpenVPN is running, pings must succeed (want='want_ok')
185	# before OpenVPN is up, pings must NOT succeed (want='want_fail')
186
187	rc=$?
188	if [ $rc = 0 ] 				# all ping OK
189	then
190	    if [ $want = "want_fail" ]		# not what we want
191	    then
192		fail "IPv$proto ping test succeeded, but needs to *fail*."
193	    fi
194	else					# ping failed
195	    if [ $want = "want_ok" ]		# not what we wanted
196	    then
197		fail "IPv$proto ping test ($bytes bytes) failed, but should succeed."
198	    fi
199	fi
200    done
201}
202
203# ----------------------------------------------------------
204# main test loop
205# ----------------------------------------------------------
206SUMMARY_OK=
207SUMMARY_FAIL=
208
209for SUF in $TEST_RUN_LIST
210do
211    # get config variables
212    eval test_run_title=\"\$RUN_TITLE_$SUF\"
213    eval openvpn_conf=\"\$OPENVPN_CONF_$SUF\"
214    eval expect_ifconfig4=\"\$EXPECT_IFCONFIG4_$SUF\"
215    eval expect_ifconfig6=\"\$EXPECT_IFCONFIG6_$SUF\"
216    eval ping4_hosts=\"\$PING4_HOSTS_$SUF\"
217    eval ping6_hosts=\"\$PING6_HOSTS_$SUF\"
218
219    echo -e "\n### test run $SUF: '$test_run_title' ###\n"
220    fail_count=0
221
222    echo "save pre-openvpn ifconfig + route"
223    get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route_pre.txt
224
225    echo -e "\nrun pre-openvpn ping tests - targets must not be reachable..."
226    run_ping_tests 4 want_fail "$ping4_hosts"
227    run_ping_tests 6 want_fail "$ping6_hosts"
228    if [ "$fail_count" = 0 ] ; then
229        echo -e "OK.\n"
230    else
231	echo -e "FAIL: make sure that ping hosts are ONLY reachable via VPN, SKIP test $SUF".
232	exit_code=31
233	continue
234    fi
235
236    echo " run openvpn $openvpn_conf"
237    echo "# src/openvpn/openvpn $openvpn_conf" >$LOGDIR/$SUF:openvpn.log
238    $RUN_SUDO "${top_builddir}/src/openvpn/openvpn" $openvpn_conf >>$LOGDIR/$SUF:openvpn.log &
239    opid=$!
240
241    # make sure openvpn client is terminated in case shell exits
242    trap "$RUN_SUDO kill $opid" 0
243    trap "$RUN_SUDO kill $opid ; trap - 0 ; exit 1" 1 2 3 15
244
245    echo "wait for connection to establish..."
246    sleep ${SETUP_TIME_WAIT:-10}
247
248    # test whether OpenVPN process is still there
249    if $RUN_SUDO kill -0 $opid
250    then :
251    else
252	echo -e "OpenVPN process has failed to start up, check log ($LOGDIR/$SUF:openvpn.log).  FAIL.\ntail of logfile follows:\n..." >&2
253	tail $LOGDIR/$SUF:openvpn.log >&2
254	trap - 0 1 2 3 15
255	exit 10
256    fi
257
258    # compare whether anything changed in ifconfig/route setup?
259    echo "save ifconfig+route"
260    get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route.txt
261
262    echo -n "compare pre-openvpn ifconfig+route with current values..."
263    if diff $LOGDIR/$SUF:ifconfig_route_pre.txt \
264	    $LOGDIR/$SUF:ifconfig_route.txt >/dev/null
265    then
266	fail "no differences between ifconfig/route before OpenVPN start and now."
267    else
268	echo -e " OK!\n"
269    fi
270
271    # expected ifconfig values in there?
272    check_ifconfig 4 "$expect_ifconfig4"
273    check_ifconfig 6 "$expect_ifconfig6"
274
275    run_ping_tests 4 want_ok "$ping4_hosts"
276    run_ping_tests 6 want_ok "$ping6_hosts"
277    echo -e "ping tests done.\n"
278
279    echo "stopping OpenVPN"
280    $RUN_SUDO kill $opid
281    wait $!
282    rc=$?
283    if [ $rc != 0 ] ; then
284	fail "OpenVPN return code $rc, expect 0"
285    fi
286
287    echo -e "\nsave post-openvpn ifconfig + route..."
288    get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route_post.txt
289
290    echo -n "compare pre- and post-openvpn ifconfig + route..."
291    if diff $LOGDIR/$SUF:ifconfig_route_pre.txt \
292	    $LOGDIR/$SUF:ifconfig_route_post.txt >$LOGDIR/$SUF:ifconfig_route_diff.txt
293    then
294	echo -e " OK.\n"
295    else
296	cat $LOGDIR/$SUF:ifconfig_route_diff.txt >&2
297	fail "differences between pre- and post-ifconfig/route"
298    fi
299    if [ "$fail_count" = 0 ] ; then
300        echo -e "test run $SUF: all tests OK.\n"
301	SUMMARY_OK="$SUMMARY_OK $SUF"
302    else
303	echo -e "test run $SUF: $fail_count test failures. FAIL.\n";
304	SUMMARY_FAIL="$SUMMARY_FAIL $SUF"
305	exit_code=30
306    fi
307done
308
309if [ -z "$SUMMARY_OK" ] ; then SUMMARY_OK=" none"; fi
310if [ -z "$SUMMARY_FAIL" ] ; then SUMMARY_FAIL=" none"; fi
311echo "Test sets succeded:$SUMMARY_OK."
312echo "Test sets failed:$SUMMARY_FAIL."
313
314# remove trap handler
315trap - 0 1 2 3 15
316exit $exit_code
317