tests/t_client.sh.in
5086d75d
 #!@SHELL@
186f9a76
 #
 # run OpenVPN client against ``test reference'' server
 # - check that ping, http, ... via tunnel works
 # - check that interface config / routes are properly cleaned after test end
 #
 # prerequisites:
 # - openvpn binary in current directory
 # - writable current directory to create subdir for logs
 # - t_client.rc in current directory OR source dir that specifies tests
 # - for "ping4" checks: fping binary in $PATH
 # - for "ping6" checks: fping6 binary in $PATH
 #
 
34cb9132
 srcdir="${srcdir:-.}"
 top_builddir="${top_builddir:-..}"
47c99000
 if [ -r "${top_builddir}"/t_client.rc ] ; then
     . "${top_builddir}"/t_client.rc
 elif [ -r "${srcdir}"/t_client.rc ] ; then
f25fe91a
     . "${srcdir}"/t_client.rc
 else
47c99000
     echo "$0: cannot find 't_client.rc' in build dir ('${top_builddir}')" >&2
     echo "$0: or source directory ('${srcdir}'). SKIPPING TEST." >&2
f25fe91a
     exit 77
 fi
 
f0892e65
 # Check for external dependencies
 which fping > /dev/null
 if [ $? -ne 0 ]; then
ebcd7549
     echo "$0: fping is not available in \$PATH" >&2
f0892e65
     exit 77
 fi
 which fping6 > /dev/null
 if [ $? -ne 0 ]; then
ebcd7549
     echo "$0: fping6 is not available in \$PATH" >&2
f0892e65
     exit 77
 fi
 
6b25b99f
 KILL_EXEC=`which kill`
 if [ $? -ne 0 ]; then
     echo "$0: kill not found in \$PATH" >&2
     exit 77
 fi
 
34cb9132
 if [ ! -x "${top_builddir}/src/openvpn/openvpn" ]
186f9a76
 then
47c99000
     echo "no (executable) openvpn binary in current build tree. FAIL." >&2
186f9a76
     exit 1
 fi
 
 if [ ! -w . ]
 then
     echo "current directory is not writable (required for logging). FAIL." >&2
     exit 1
 fi
 
 if [ -z "$CA_CERT" ] ; then
     echo "CA_CERT not defined in 't_client.rc'. SKIP test." >&2
6f1e61b4
     exit 77
186f9a76
 fi
 
 if [ -z "$TEST_RUN_LIST" ] ; then
     echo "TEST_RUN_LIST empty, no tests defined.  SKIP test." >&2
6f1e61b4
     exit 77
186f9a76
 fi
 
6b25b99f
 # Ensure PREFER_KSU is in a known state
 PREFER_KSU="${PREFER_KSU:-0}"
 
186f9a76
 # make sure we have permissions to run ifconfig/route from OpenVPN
 # can't use "id -u" here - doesn't work on Solaris
 ID=`id`
 if expr "$ID" : "uid=0" >/dev/null
 then :
 else
6b25b99f
     if [ "${PREFER_KSU}" -eq 1 ];
     then
         # Check if we have a valid kerberos ticket
         klist -l 1>/dev/null 2>/dev/null
         if [ $? -ne 0 ];
         then
             # No kerberos ticket found, skip ksu and fallback to RUN_SUDO
             PREFER_KSU=0
             echo "$0: No Kerberos ticket available.  Will not use ksu."
         else
             RUN_SUDO="ksu -q -e"
         fi
     fi
 
9c6ee9d1
     if [ -z "$RUN_SUDO" ]
     then
         echo "$0: this test must run be as root, or RUN_SUDO=... " >&2
         echo "      must be set correctly in 't_client.rc'. SKIP." >&2
         exit 77
f40f10ea
     else
         # We have to use sudo. Make sure that we (hopefully) do not have
         # to ask the users password during the test. This is done to
         # prevent timing issues, e.g. when the waits for openvpn to start
8ca29af7
 	if $RUN_SUDO $KILL_EXEC -0 $$
 	then
 	    echo "$0: $RUN_SUDO $KILL_EXEC -0 succeeded, good."
 	else
 	    echo "$0: $RUN_SUDO $KILL_EXEC -0 failed, cannot go on. SKIP." >&2
 	    exit 77
 	fi
9c6ee9d1
     fi
186f9a76
 fi
 
 LOGDIR=t_client-`hostname`-`date +%Y%m%d-%H%M%S`
 if mkdir $LOGDIR
 then :
 else
     echo "can't create log directory '$LOGDIR'. FAIL." >&2
     exit 1
 fi
 
 exit_code=0
 
 # ----------------------------------------------------------
 # helper functions
 # ----------------------------------------------------------
6b25b99f
 
186f9a76
 # print failure message, increase FAIL counter
 fail()
 {
     echo ""
     echo "FAIL: $@" >&2
     fail_count=$(( $fail_count + 1 ))
 }
 
 # print "all interface IP addresses" + "all routes"
 # this is higly system dependent...
 get_ifconfig_route()
 {
5086d75d
     # linux / iproute2? (-> if configure got a path)
722027a2
     if [ -n "@IPROUTE@" ]
186f9a76
     then
 	echo "-- linux iproute2 --"
5086d75d
 	@IPROUTE@ addr show     | grep -v valid_lft
 	@IPROUTE@ route show
1e3a1786
 	@IPROUTE@ -o -6 route show | grep -v ' cache' | sed -E -e 's/ expires [0-9]*sec//' -e 's/ (mtu|hoplimit|cwnd|ssthresh) [0-9]+//g' -e 's/ (rtt|rttvar) [0-9]+ms//g'
186f9a76
 	return
     fi
 
     # try uname
     case `uname -s` in
 	Linux)
 	   echo "-- linux / ifconfig --"
5086d75d
 	   LANG=C @IFCONFIG@ -a |egrep  "( addr:|encap:)"
 	   LANG=C @NETSTAT@ -rn -4 -6
186f9a76
 	   return
 	   ;;
 	FreeBSD|NetBSD|Darwin)
 	   echo "-- FreeBSD/NetBSD/Darwin [MacOS X] --"
5086d75d
 	   @IFCONFIG@ -a | egrep "(flags=|inet)"
 	   @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$NF }'
186f9a76
 	   return
 	   ;;
 	OpenBSD)
 	   echo "-- OpenBSD --"
5086d75d
 	   @IFCONFIG@ -a | egrep "(flags=|inet)" | \
186f9a76
 		sed -e 's/pltime [0-9]*//' -e 's/vltime [0-9]*//'
5086d75d
 	   @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$NF }'
186f9a76
 	   return
 	   ;;
 	SunOS)
 	   echo "-- Solaris --"
5086d75d
 	   @IFCONFIG@ -a | egrep "(flags=|inet)"
 	   @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$6 }'
186f9a76
 	   return
 	   ;;
a637016e
 	AIX)
 	   echo "-- AIX --"
 	   @IFCONFIG@ -a | egrep "(flags=|inet)"
 	   @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$6 }'
 	   return
 	   ;;
186f9a76
     esac
 
     echo "get_ifconfig_route(): no idea how to get info on your OS.  FAIL." >&2
     exit 20
 }
 
 # ----------------------------------------------------------
 # check ifconfig
 #  arg1: "4" or "6" -> for message
 #  arg2: IPv4/IPv6 address that must show up in out of "get_ifconfig_route"
 check_ifconfig()
 {
     proto=$1 ; shift
     expect_list="$@"
 
     if [ -z "$expect_list" ] ; then return ; fi
 
     for expect in $expect_list
     do
 	if get_ifconfig_route | fgrep "$expect" >/dev/null
 	then :
 	else
 	    fail "check_ifconfig(): expected IPv$proto address '$expect' not found in ifconfig output."
 	fi
     done
 }
 
 # ----------------------------------------------------------
 # run pings
 #  arg1: "4" or "6" -> fping/fing6
 #  arg2: "want_ok" or "want_fail" (expected ping result)
 #  arg3... -> fping arguments (host list)
 run_ping_tests()
 {
     proto=$1 ; want=$2 ; shift ; shift
     targetlist="$@"
 
     # "no targets" is fine
     if [ -z "$targetlist" ] ; then return ; fi
 
     case $proto in
 	4) cmd=fping ;;
 	6) cmd=fping6 ;;
 	*) echo "internal error in run_ping_tests arg 1: '$proto'" >&2
 	   exit 1 ;;
     esac
 
     case $want in
 	want_ok)   sizes_list="64 1440 3000" ;;
 	want_fail) sizes_list="64" ;;
     esac
 
     for bytes in $sizes_list
     do
 	echo "run IPv$proto ping tests ($want), $bytes byte packets..."
 
6f17e18b
 	echo "$cmd -b $bytes -C 20 -p 250 -q $FPING_EXTRA_ARGS $targetlist" >>$LOGDIR/$SUF:fping.out
 	$cmd -b $bytes -C 20 -p 250 -q $FPING_EXTRA_ARGS $targetlist >>$LOGDIR/$SUF:fping.out 2>&1
186f9a76
 
 	# while OpenVPN is running, pings must succeed (want='want_ok')
 	# before OpenVPN is up, pings must NOT succeed (want='want_fail')
 
 	rc=$?
 	if [ $rc = 0 ] 				# all ping OK
 	then
 	    if [ $want = "want_fail" ]		# not what we want
 	    then
 		fail "IPv$proto ping test succeeded, but needs to *fail*."
 	    fi
 	else					# ping failed
 	    if [ $want = "want_ok" ]		# not what we wanted
 	    then
 		fail "IPv$proto ping test ($bytes bytes) failed, but should succeed."
 	    fi
 	fi
     done
 }
 
 # ----------------------------------------------------------
 # main test loop
 # ----------------------------------------------------------
64a6bdf7
 SUMMARY_OK=
 SUMMARY_FAIL=
 
186f9a76
 for SUF in $TEST_RUN_LIST
 do
     # get config variables
8fedf86a
     eval test_prep=\"\$PREPARE_$SUF\"
bae1ad70
     eval test_postinit=\"\$POSTINIT_CMD_$SUF\"
8fedf86a
     eval test_cleanup=\"\$CLEANUP_$SUF\"
9c6ee9d1
     eval test_run_title=\"\$RUN_TITLE_$SUF\"
186f9a76
     eval openvpn_conf=\"\$OPENVPN_CONF_$SUF\"
     eval expect_ifconfig4=\"\$EXPECT_IFCONFIG4_$SUF\"
     eval expect_ifconfig6=\"\$EXPECT_IFCONFIG6_$SUF\"
     eval ping4_hosts=\"\$PING4_HOSTS_$SUF\"
     eval ping6_hosts=\"\$PING6_HOSTS_$SUF\"
 
df0b00c2
     # If EXCEPT_IFCONFIG* variables for this test are missing, run an --up
     # script to generate them dynamically.
     if [ -z "$expect_ifconfig4" ] || [ -z "$expect_ifconfig6" ]; then
2af1fa8b
         up="--setenv TESTNUM $SUF --setenv TOP_BUILDDIR ${top_builddir} --script-security 2 --up ${srcdir}/update_t_client_ips.sh"
dd9a4f0e
     else
         up=""
df0b00c2
     fi
 
9c6ee9d1
     echo -e "\n### test run $SUF: '$test_run_title' ###\n"
     fail_count=0
 
8fedf86a
     if [ -n "$test_prep" ]; then
         echo -e "running preparation: '$test_prep'"
         eval $test_prep
     fi
 
9c6ee9d1
     echo "save pre-openvpn ifconfig + route"
     get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route_pre.txt
 
186f9a76
     echo -e "\nrun pre-openvpn ping tests - targets must not be reachable..."
     run_ping_tests 4 want_fail "$ping4_hosts"
     run_ping_tests 6 want_fail "$ping6_hosts"
     if [ "$fail_count" = 0 ] ; then
         echo -e "OK.\n"
     else
 	echo -e "FAIL: make sure that ping hosts are ONLY reachable via VPN, SKIP test $SUF".
 	exit_code=31
 	continue
     fi
 
e0926ebf
     pidfile="${top_builddir}/tests/$LOGDIR/openvpn-$SUF.pid"
df0b00c2
     openvpn_conf="$openvpn_conf --writepid $pidfile $up"
34cb9132
     echo " run openvpn $openvpn_conf"
cc43956c
     echo "# src/openvpn/openvpn $openvpn_conf" >$LOGDIR/$SUF:openvpn.log
3712322e
     umask 022
cc43956c
     $RUN_SUDO "${top_builddir}/src/openvpn/openvpn" $openvpn_conf >>$LOGDIR/$SUF:openvpn.log &
3712322e
     sudopid=$!
186f9a76
 
3712322e
     # Check if OpenVPN has initialized before continuing.  It will check every 3rd second up
     # to $ovpn_init_check times.
     ovpn_init_check=10
     ovpn_init_success=0
     while [ $ovpn_init_check -gt 0 ];
     do
        sleep 3  # Wait for OpenVPN to initialize and have had time to write the pid file
38f98fdc
        grep "Initialization Sequence Completed" $LOGDIR/$SUF:openvpn.log >/dev/null
3712322e
        if [ $? -eq 0 ]; then
            ovpn_init_check=0
            ovpn_init_success=1
        fi
        ovpn_init_check=$(( $ovpn_init_check - 1 ))
     done
186f9a76
 
3712322e
     opid=`cat $pidfile`
     if [ -n "$opid" ]; then
         echo "  OpenVPN running with PID $opid"
186f9a76
     else
3712322e
         echo "  Could not read OpenVPN PID file" >&2
     fi
 
     # If OpenVPN did not start
     if [ $ovpn_init_success -ne 1 -o -z "$opid" ]; then
         echo "$0:  OpenVPN did not initialize in a reasonable time" >&2
         if [ -n "$opid" ]; then
            $RUN_SUDO $KILL_EXEC $opid
         fi
         $RUN_SUDO $KILL_EXEC $sudopid
a7b02f7f
 	echo "tail -5 $SUF:openvpn.log" >&2
 	tail -5 $LOGDIR/$SUF:openvpn.log >&2
 	echo -e "\nFAIL. skip rest of sub-tests for test run $SUF.\n" >&2
186f9a76
 	trap - 0 1 2 3 15
a7b02f7f
 	SUMMARY_FAIL="$SUMMARY_FAIL $SUF"
 	exit_code=30
 	continue
186f9a76
     fi
 
3712322e
     # make sure openvpn client is terminated in case shell exits
     trap "$RUN_SUDO $KILL_EXEC $opid" 0
     trap "$RUN_SUDO $KILL_EXEC $opid ; trap - 0 ; exit 1" 1 2 3 15
 
186f9a76
     # compare whether anything changed in ifconfig/route setup?
     echo "save ifconfig+route"
     get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route.txt
 
     echo -n "compare pre-openvpn ifconfig+route with current values..."
     if diff $LOGDIR/$SUF:ifconfig_route_pre.txt \
 	    $LOGDIR/$SUF:ifconfig_route.txt >/dev/null
     then
 	fail "no differences between ifconfig/route before OpenVPN start and now."
     else
 	echo -e " OK!\n"
     fi
 
bae1ad70
     # post init script needed?
     if [ -n "$test_postinit" ]; then
         echo -e "running post-init cmd: '$test_postinit'"
         eval $test_postinit
     fi
 
186f9a76
     # expected ifconfig values in there?
     check_ifconfig 4 "$expect_ifconfig4"
     check_ifconfig 6 "$expect_ifconfig6"
 
     run_ping_tests 4 want_ok "$ping4_hosts"
     run_ping_tests 6 want_ok "$ping6_hosts"
     echo -e "ping tests done.\n"
 
     echo "stopping OpenVPN"
6b25b99f
     $RUN_SUDO $KILL_EXEC $opid
186f9a76
     wait $!
     rc=$?
     if [ $rc != 0 ] ; then
 	fail "OpenVPN return code $rc, expect 0"
     fi
 
     echo -e "\nsave post-openvpn ifconfig + route..."
     get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route_post.txt
 
     echo -n "compare pre- and post-openvpn ifconfig + route..."
     if diff $LOGDIR/$SUF:ifconfig_route_pre.txt \
 	    $LOGDIR/$SUF:ifconfig_route_post.txt >$LOGDIR/$SUF:ifconfig_route_diff.txt
     then
 	echo -e " OK.\n"
     else
 	cat $LOGDIR/$SUF:ifconfig_route_diff.txt >&2
 	fail "differences between pre- and post-ifconfig/route"
     fi
     if [ "$fail_count" = 0 ] ; then
         echo -e "test run $SUF: all tests OK.\n"
64a6bdf7
 	SUMMARY_OK="$SUMMARY_OK $SUF"
186f9a76
     else
 	echo -e "test run $SUF: $fail_count test failures. FAIL.\n";
64a6bdf7
 	SUMMARY_FAIL="$SUMMARY_FAIL $SUF"
186f9a76
 	exit_code=30
     fi
8fedf86a
 
     if [ -n "$test_cleanup" ]; then
         echo -e "cleaning up: '$test_cleanup'"
         eval $test_cleanup
     fi
 
186f9a76
 done
 
64a6bdf7
 if [ -z "$SUMMARY_OK" ] ; then SUMMARY_OK=" none"; fi
 if [ -z "$SUMMARY_FAIL" ] ; then SUMMARY_FAIL=" none"; fi
 echo "Test sets succeded:$SUMMARY_OK."
 echo "Test sets failed:$SUMMARY_FAIL."
 
186f9a76
 # remove trap handler
 trap - 0 1 2 3 15
 exit $exit_code