mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-08-08 09:21:48 +08:00
Use `command -v` instead of `which` because: * It's built into the shell, avoiding an external dependency on the `which` package on Fedora 44. * It works across all POSIX-compliant shells. * It's standard across Linux distributions. For example, Fedora 44 lacks the `which` package by default. Signed-off-by: Dimitri Papadopoulos <3350651-DimitriPapadopoulos@users.noreply.gitlab.com>
159 lines
4.9 KiB
Bash
Executable File
159 lines
4.9 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Copyright (C) 2026 Nikos Mavrogiannopoulos
|
|
#
|
|
# This file is part of ocserv.
|
|
#
|
|
# ocserv is free software; you can redistribute it and/or modify it
|
|
# under the terms of the GNU General Public License as published by the
|
|
# Free Software Foundation; either version 2 of the License, or (at
|
|
# your option) any later version.
|
|
#
|
|
# ocserv is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
# Regression test for issue #638: cstp_send() must not loop forever when
|
|
# the peer stops draining the TCP socket (e.g. a mobile client roaming
|
|
# between networks and the TCP connection going silently dead).
|
|
#
|
|
# The config sets no-udp=true so all VPN data travels over TLS/CSTP,
|
|
# ensuring that cstp_send() is on the critical path.
|
|
#
|
|
# Method:
|
|
# 1. Connect an openconnect client (TLS-only, no DTLS).
|
|
# 2. Freeze openconnect with SIGSTOP so it can no longer drain its TCP
|
|
# receive buffer.
|
|
# 3. Flood large ICMP packets from the server namespace toward the client
|
|
# VPN IP. Each packet enters the TUN, is read by the worker, and
|
|
# handed to cstp_send(); with a frozen receiver gnutls_record_send()
|
|
# returns GNUTLS_E_AGAIN.
|
|
# 4. Assert that the worker disconnects the session within
|
|
# DEFAULT_SOCKET_TIMEOUT + margin rather than looping forever.
|
|
|
|
OCCTL="${OCCTL:-../src/occtl/occtl}"
|
|
SERV="${SERV:-../src/ocserv}"
|
|
srcdir=${srcdir:-.}
|
|
PIDFILE=ocserv-pid.$$.tmp
|
|
CLIPID=oc-pid.$$.tmp
|
|
OCCTL_SOCKET=./occtl-cstp-hang-$$.socket
|
|
PATH=${PATH}:/usr/sbin
|
|
IP=$(command -v ip)
|
|
|
|
. "$(dirname "$0")"/common.sh
|
|
|
|
eval "${GETPORT}"
|
|
|
|
if test "$(id -u)" != "0"; then
|
|
echo "This test must be run as root"
|
|
exit 77
|
|
fi
|
|
|
|
if test -z "${IP}"; then
|
|
echo "no ip tool found"
|
|
exit 77
|
|
fi
|
|
|
|
echo "Testing that cstp_send() does not hang when the peer freezes (issue #638)..."
|
|
|
|
function finish {
|
|
echo " * Cleaning up..."
|
|
# Resume openconnect before killing it so cleanup_client_server works cleanly
|
|
test -f "${CLIPID}" && kill -CONT "$(cat "${CLIPID}")" >/dev/null 2>&1
|
|
cleanup_client_server
|
|
}
|
|
trap finish EXIT
|
|
|
|
. "$(dirname "$0")"/random-net.sh
|
|
. "$(dirname "$0")"/ns.sh
|
|
|
|
update_config cstp-send-hang.config
|
|
if test "$VERBOSE" = 1; then
|
|
DEBUG="-d 3"
|
|
fi
|
|
|
|
${CMDNS2} ${SERV} -p ${PIDFILE} -f -c ${CONFIG} ${DEBUG} & PID=$!
|
|
|
|
sleep 3
|
|
|
|
echo " * Connecting to ${ADDRESS}:${PORT} (TLS only, no DTLS)..."
|
|
echo "test" | ${CMDNS1} ${OPENCONNECT} \
|
|
${ADDRESS}:${PORT} \
|
|
-u test \
|
|
--passwd-on-stdin \
|
|
--servercert=pin-sha256:xp3scfzy3rOQsv9NcOve/8YVVv+pHr4qNCXEXrNl5s8= \
|
|
-s "${srcdir}/scripts/vpnc-script" \
|
|
--pid-file="${CLIPID}" \
|
|
-b
|
|
|
|
if test $? != 0; then
|
|
fail $PID "Could not connect to server"
|
|
fi
|
|
|
|
sleep 2
|
|
|
|
if test ! -f "${CLIPID}"; then
|
|
fail $PID "openconnect did not write pid file"
|
|
fi
|
|
|
|
echo " * Verifying VPN is up..."
|
|
${CMDNS1} ping -c 3 ${VPNADDR}
|
|
if test $? != 0; then
|
|
fail $PID "VPN ping failed — connection not established"
|
|
fi
|
|
|
|
# Obtain the IP address assigned to the client inside the VPN.
|
|
# cstp_send() is exercised when the worker sends packets destined for
|
|
# this address out through the TUN.
|
|
OCCTL_JSON=$(${OCCTL} -s "${OCCTL_SOCKET}" -j show user test 2>/dev/null)
|
|
if test "$VERBOSE" = 1; then
|
|
echo " * occtl JSON: ${OCCTL_JSON}"
|
|
fi
|
|
VPN_CLI_IP=$(echo "${OCCTL_JSON}" | jq -r '.[0].IPv4' 2>/dev/null)
|
|
|
|
if test -z "${VPN_CLI_IP}" || test "${VPN_CLI_IP}" = "null"; then
|
|
fail $PID "Could not determine client VPN IP from occtl (JSON: ${OCCTL_JSON})"
|
|
fi
|
|
echo " * Client VPN IP: ${VPN_CLI_IP}"
|
|
|
|
OC_PID=$(cat "${CLIPID}")
|
|
|
|
echo " * Freezing openconnect (simulating silent network loss)..."
|
|
kill -STOP "${OC_PID}"
|
|
|
|
# Flood large ICMP packets from the server namespace toward the client VPN IP.
|
|
# Each packet travels: server routing → TUN → worker → cstp_send() → TLS/TCP.
|
|
# Because openconnect is frozen the TCP receive buffer fills and
|
|
# gnutls_record_send() starts returning GNUTLS_E_AGAIN.
|
|
echo " * Flooding server→client to trigger GNUTLS_E_AGAIN in cstp_send()..."
|
|
${CMDNS2} ping -f -s 1400 -c 100000 ${VPN_CLI_IP} >/dev/null 2>&1 &
|
|
FLOODPID=$!
|
|
|
|
# Wait up to DEFAULT_SOCKET_TIMEOUT (10 s) + 10 s margin for the worker to
|
|
# hit the poll() deadline and disconnect. On unfixed code the worker loops
|
|
# forever and the user remains visible in occtl at the deadline.
|
|
echo " * Waiting for worker to time out and disconnect..."
|
|
DEADLINE=$(($(date +%s) + 20))
|
|
WORKER_GONE=false
|
|
while test "$(date +%s)" -lt "${DEADLINE}"; do
|
|
sleep 2
|
|
if ! ${OCCTL} -s "${OCCTL_SOCKET}" show user test >/dev/null 2>&1; then
|
|
WORKER_GONE=true
|
|
break
|
|
fi
|
|
done
|
|
|
|
kill "${FLOODPID}" 2>/dev/null
|
|
wait "${FLOODPID}" 2>/dev/null
|
|
|
|
if test "${WORKER_GONE}" != "true"; then
|
|
fail $PID "Worker did not disconnect frozen client within timeout (issue #638)"
|
|
fi
|
|
|
|
echo " * Worker disconnected frozen client as expected."
|
|
exit 0
|