#!@SHELL@ # # 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: fping (4.0+) or fping6 binary in $PATH # # by changing this to 1 we can force automated builds to fail # that are expected to have all the prerequisites TCLIENT_SKIP_RC="${TCLIENT_SKIP_RC:-77}" srcdir="${srcdir:-.}" top_builddir="${top_builddir:-..}" openvpn="${openvpn:-${top_builddir}/src/openvpn/openvpn}" if [ -r "${top_builddir}"/t_client.rc ] ; then . "${top_builddir}"/t_client.rc elif [ -r "${srcdir}"/t_client.rc ] ; then . "${srcdir}"/t_client.rc else echo "$0: cannot find 't_client.rc' in build dir ('${top_builddir}')" >&2 echo "$0: or source directory ('${srcdir}'). SKIPPING TEST." >&2 exit "${TCLIENT_SKIP_RC}" fi # Check for external dependencies FPING="fping" FPING6="fping6" which fping > /dev/null if [ $? -ne 0 ]; then echo "$0: fping is not available in \$PATH" >&2 exit "${TCLIENT_SKIP_RC}" fi which fping6 > /dev/null if [ $? -ne 0 ]; then echo "$0: fping6 is not available in \$PATH, assuming fping 4.0 or later" >&2 FPING="fping -4" FPING6="fping -6" fi KILL_EXEC=`which kill` if [ $? -ne 0 ]; then echo "$0: kill not found in \$PATH" >&2 exit "${TCLIENT_SKIP_RC}" fi if [ ! -x "${openvpn}" ] then echo "no (executable) openvpn binary in current build tree. FAIL." >&2 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 exit "${TCLIENT_SKIP_RC}" fi if [ -z "$TEST_RUN_LIST" ] ; then echo "TEST_RUN_LIST empty, no tests defined. SKIP test." >&2 exit "${TCLIENT_SKIP_RC}" fi # Ensure PREFER_KSU is in a known state PREFER_KSU="${PREFER_KSU:-0}" # 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 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 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 "${TCLIENT_SKIP_RC}" 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 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 "${TCLIENT_SKIP_RC}" fi fi 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 # verbosity, defaults to "1" V="${V:-1}" exit_code=0 # ---------------------------------------------------------- # helper functions # ---------------------------------------------------------- # output progress information # depending on verbosity level, collect & print only on failure output_start() { case $V in 0) outbuf="" ;; # no per-test output at all 1) echo -e "$@" # compact, details only on failure outbuf="\n" ;; *) echo -e "\n$@\n" ;; # print all, with a bit formatting esac } output() { NO_NL=''; if [ "X$1" = "X-n" ] ; then NO_NL=$1 ; shift ; fi case $V in 0) ;; # no per-test output at all 1) outbuf="$outbuf$@" # print details only on failure test -z "$NO_NL" && outbuf="$outbuf\n" ;; *) echo -e $NO_NL "$@" ;; # print everything esac } # print failure message, increase FAIL counter fail() { output "FAIL: $@\n" fail_count=$(( $fail_count + 1 )) } # print "all interface IP addresses" + "all routes" # this is higly system dependent... get_ifconfig_route() { # linux / iproute2? (-> if configure got a path) if [ -n "@IPROUTE@" ] then echo "-- linux iproute2 --" @IPROUTE@ addr show | grep -v valid_lft @IPROUTE@ route show @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' return fi # try uname case `uname -s` in Linux) echo "-- linux / ifconfig --" LANG=C @IFCONFIG@ -a |egrep "( addr:|encap:)" LANG=C @NETSTAT@ -rn -4 -6 return ;; FreeBSD|NetBSD|Darwin) echo "-- FreeBSD/NetBSD/Darwin [MacOS X] --" @IFCONFIG@ -a | egrep "(flags=|inet)" @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$NF }' return ;; OpenBSD) echo "-- OpenBSD --" @IFCONFIG@ -a | egrep "(flags=|inet)" | \ sed -e 's/pltime [0-9]*//' -e 's/vltime [0-9]*//' @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$NF }' return ;; SunOS) echo "-- Solaris --" @IFCONFIG@ -a | egrep "(flags=|inet)" @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$6 }' return ;; AIX) echo "-- AIX --" @IFCONFIG@ -a | egrep "(flags=|inet)" @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$6 }' return ;; 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 output "run IPv$proto ping tests ($want), $bytes byte packets..." echo "$cmd -b $bytes -C 20 -p 250 -q $fping_args $targetlist" >>$LOGDIR/$SUF:fping.out $cmd -b $bytes -C 20 -p 250 -q $fping_args $targetlist >>$LOGDIR/$SUF:fping.out 2>&1 # 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 # ---------------------------------------------------------- SUMMARY_OK= SUMMARY_SKIP= SUMMARY_FAIL= for SUF in $TEST_RUN_LIST do # get config variables eval test_prep=\"\$PREPARE_$SUF\" eval test_check_skip=\"\$CHECK_SKIP_$SUF\" eval test_postinit=\"\$POSTINIT_CMD_$SUF\" eval test_cleanup=\"\$CLEANUP_$SUF\" eval test_run_title=\"\$RUN_TITLE_$SUF\" 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\" eval fping_args=\"\$FPING_EXTRA_ARGS \$FPING_ARGS_$SUF\" # 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 up="--setenv TESTNUM $SUF --setenv TOP_BUILDDIR ${top_builddir} --script-security 2 --up ${srcdir}/update_t_client_ips.sh" else up="" fi output_start "### test run $SUF: '$test_run_title' ###" fail_count=0 if [ -n "$test_check_skip" ]; then output "check whether we need to skip: '$test_check_skip'" if eval $test_check_skip; then : else output "skip check failed, SKIP test $SUF." SUMMARY_SKIP="$SUMMARY_SKIP $SUF" echo -e "$outbuf" ; continue fi fi if [ -n "$test_prep" ]; then output "running preparation: '$test_prep'" eval $test_prep fi output "save pre-openvpn ifconfig + route" get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route_pre.txt output "\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 output "OK.\n" else fail "make sure that ping hosts are ONLY reachable via VPN, SKIP test $SUF." SUMMARY_FAIL="$SUMMARY_FAIL $SUF" exit_code=31 echo -e "$outbuf" ; continue fi pidfile="${top_builddir}/tests/$LOGDIR/openvpn-$SUF.pid" openvpn_conf="$openvpn_conf --writepid $pidfile $up" output " run openvpn $openvpn_conf" echo "# ${openvpn} $openvpn_conf" >$LOGDIR/$SUF:openvpn.log umask 022 $RUN_SUDO "${openvpn}" $openvpn_conf >>$LOGDIR/$SUF:openvpn.log & sudopid=$! # 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 grep "Initialization Sequence Completed" $LOGDIR/$SUF:openvpn.log >/dev/null if [ $? -eq 0 ]; then ovpn_init_check=0 ovpn_init_success=1 fi ovpn_init_check=$(( $ovpn_init_check - 1 )) done opid=`cat $pidfile` if [ -n "$opid" ]; then output " OpenVPN running with PID $opid" else output " Could not read OpenVPN PID file" fi # If OpenVPN did not start if [ $ovpn_init_success -ne 1 -o -z "$opid" ]; then output "$0: OpenVPN did not initialize in a reasonable time" if [ -n "$opid" ]; then $RUN_SUDO $KILL_EXEC $opid fi $RUN_SUDO $KILL_EXEC $sudopid output "tail -5 $SUF:openvpn.log" output "`tail -5 $LOGDIR/$SUF:openvpn.log`" fail "skip rest of sub-tests for test run $SUF." trap - 0 1 2 3 15 SUMMARY_FAIL="$SUMMARY_FAIL $SUF" exit_code=30 echo -e "$outbuf" ; continue fi # 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 # compare whether anything changed in ifconfig/route setup? output "save ifconfig+route" get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route.txt output -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 output " OK!\n" fi # post init script needed? if [ -n "$test_postinit" ]; then output "running post-init cmd: '$test_postinit'" eval $test_postinit fi # 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" output "ping tests done.\n" output "stopping OpenVPN" $RUN_SUDO $KILL_EXEC $opid wait $! rc=$? if [ $rc != 0 ] ; then fail "OpenVPN return code $rc, expect 0" fi output "\nsave post-openvpn ifconfig + route..." get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route_post.txt output -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 output " OK.\n" else output "\n\n" "`cat $LOGDIR/$SUF:ifconfig_route_diff.txt`" "\n" fail "differences between pre- and post-ifconfig/route." fi if [ "$fail_count" = 0 ] ; then output "test run $SUF: all tests OK.\n" SUMMARY_OK="$SUMMARY_OK $SUF" else if [ "$V" -gt 0 ] ; then echo -e -n "$outbuf" echo -e "test run $SUF: $fail_count test failures. FAIL.\n" fi SUMMARY_FAIL="$SUMMARY_FAIL $SUF" exit_code=30 fi if [ -n "$test_cleanup" ]; then echo -e "cleaning up: '$test_cleanup'" eval $test_cleanup fi done if [ -z "$SUMMARY_OK" ] ; then SUMMARY_OK=" none"; fi if [ -z "$SUMMARY_SKIP" ] ; then SUMMARY_SKIP=" none"; fi if [ -z "$SUMMARY_FAIL" ] ; then SUMMARY_FAIL=" none"; fi echo "Test sets succeeded:$SUMMARY_OK." echo "Test sets skipped:$SUMMARY_SKIP." echo "Test sets failed:$SUMMARY_FAIL." # remove trap handler trap - 0 1 2 3 15 exit $exit_code