Commit bc51d371 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'selftests-mptcp-skip-tests-not-supported-by-old-kernels-part-2'

Matthieu Baerts says:

====================
selftests: mptcp: skip tests not supported by old kernels (part 2)

After a few years of increasing test coverage in the MPTCP selftests, we
realised [1] the last version of the selftests is supposed to run on old
kernels without issues.

Supporting older versions is not that easy for this MPTCP case: these
selftests are often validating the internals by checking packets that
are exchanged, when some MIB counters are incremented after some
actions, how connections are getting opened and closed in some cases,
etc. In other words, it is not limited to the socket interface between
the userspace and the kernelspace.

In addition to that, the current MPTCP selftests run a lot of different
sub-tests but the TAP13 protocol used in the selftests don't support
sub-tests: one failure in sub-tests implies that the whole selftest is
seen as failed at the end because sub-tests are not tracked. It is then
important to skip sub-tests not supported by old kernels.

To minimise the modifications and reduce the complexity to support old
versions, the idea is to look at external signs and skip the whole
selftests or just some sub-tests before starting them. This cannot be
applied in all cases.

This second part focuses on marking different sub-tests as skipped if
some MPTCP features are not supported. A few techniques are used here:

- Before starting some tests:

  - Check if a file (sysctl knob) is present: that's what patch 13/14 is
    doing for the userspace PM feature.

  - Check if a symbol is present in /proc/kallsyms: patch 1/14 adds some
    helpers in mptcp_lib.sh to ease its use. Then these helpers are used
    in patches 2, 3, 4, 10, 11 and 14/14.

  - Set a flag and get the status to check if a feature is supported:
    patch 8/14 is doing that with the 'fullmesh' flag.

- After having launched the tests:

  - Retrieve the counters after a test and check if they are different
    than 0. Similar to the check with the flag, that's not ideal but in
    this case, the counters were already present before the introduction
    of MPTCP but they have been supported by MPTCP sockets only later.
    Patches 5 and 6/14 are using this technique.

Before skipping tests, SELFTESTS_MPTCP_LIB_EXPECT_ALL_FEATURES env var
value is checked: if it is set to 1, the test is marked as "failed"
instead of "skipped". MPTCP public CI expects to have all features
supported and it sets this env var to 1 to catch regressions in these
new checks.

Patches 7/14 and 9/14 are a bit different because they don't skip tests:

- Patch 7/14 retrieves the default values instead of using hardcoded
  ones because these default values have been modified at some points.
  Then the comparisons are done with the default values.

- patch 9/14 relaxes the expected returned size from MPTCP's getsockopt
  because the different structures gathering various info can get new
  fields and get bigger over time. We cannot expect that the userspace
  is using the same structure as the kernel.

Patch 12/14 marks the test as "skipped" instead of "failed" if the "ip"
tool is not available.

In this second part, the "mptcp_join" selftest is not modified yet. This
will come soon after in the third part with quite a few patches.

Link: https://lore.kernel.org/stable/CA+G9fYtDGpgT4dckXD-y-N92nqUxuvue_7AtDdBcHrbOMsDZLg@mail.gmail.com/ [1]
Link: https://github.com/multipath-tcp/mptcp_net-next/issues/368
====================

Link: https://lore.kernel.org/r/20230608-upstream-net-20230608-mptcp-selftests-support-old-kernels-part-2-v1-0-20997a6fd841@tessares.netSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 04c55383 626cb7a5
CONFIG_KALLSYMS=y
CONFIG_MPTCP=y
CONFIG_IPV6=y
CONFIG_MPTCP_IPV6=y
......
......@@ -55,16 +55,20 @@ __chk_nr()
{
local command="$1"
local expected=$2
local msg nr
local msg="$3"
local skip="${4:-SKIP}"
local nr
shift 2
msg=$*
nr=$(eval $command)
printf "%-50s" "$msg"
if [ $nr != $expected ]; then
echo "[ fail ] expected $expected found $nr"
ret=$test_cnt
if [ $nr = "$skip" ] && ! mptcp_lib_expect_all_features; then
echo "[ skip ] Feature probably not supported"
else
echo "[ fail ] expected $expected found $nr"
ret=$test_cnt
fi
else
echo "[ ok ]"
fi
......@@ -76,12 +80,12 @@ __chk_msk_nr()
local condition=$1
shift 1
__chk_nr "ss -inmHMN $ns | $condition" $*
__chk_nr "ss -inmHMN $ns | $condition" "$@"
}
chk_msk_nr()
{
__chk_msk_nr "grep -c token:" $*
__chk_msk_nr "grep -c token:" "$@"
}
wait_msk_nr()
......@@ -119,37 +123,26 @@ wait_msk_nr()
chk_msk_fallback_nr()
{
__chk_msk_nr "grep -c fallback" $*
__chk_msk_nr "grep -c fallback" "$@"
}
chk_msk_remote_key_nr()
{
__chk_msk_nr "grep -c remote_key" $*
__chk_msk_nr "grep -c remote_key" "$@"
}
__chk_listen()
{
local filter="$1"
local expected=$2
local msg="$3"
shift 2
msg=$*
nr=$(ss -N $ns -Ml "$filter" | grep -c LISTEN)
printf "%-50s" "$msg"
if [ $nr != $expected ]; then
echo "[ fail ] expected $expected found $nr"
ret=$test_cnt
else
echo "[ ok ]"
fi
__chk_nr "ss -N $ns -Ml '$filter' | grep -c LISTEN" "$expected" "$msg" 0
}
chk_msk_listen()
{
lport=$1
local msg="check for listen socket"
# destination port search should always return empty list
__chk_listen "dport $lport" 0 "listen match for dport $lport"
......@@ -167,10 +160,9 @@ chk_msk_listen()
chk_msk_inuse()
{
local expected=$1
local msg="$2"
local listen_nr
shift 1
listen_nr=$(ss -N "${ns}" -Ml | grep -c LISTEN)
expected=$((expected + listen_nr))
......@@ -181,7 +173,7 @@ chk_msk_inuse()
sleep 0.1
done
__chk_nr get_msk_inuse $expected $*
__chk_nr get_msk_inuse $expected "$msg" 0
}
# $1: ns, $2: port
......
......@@ -144,6 +144,7 @@ cleanup()
}
mptcp_lib_check_mptcp
mptcp_lib_check_kallsyms
ip -Version > /dev/null 2>&1
if [ $? -ne 0 ];then
......@@ -695,6 +696,15 @@ run_test_transparent()
return 0
fi
# IP(V6)_TRANSPARENT has been added after TOS support which came with
# the required infrastructure in MPTCP sockopt code. To support TOS, the
# following function has been exported (T). Not great but better than
# checking for a specific kernel version.
if ! mptcp_lib_kallsyms_has "T __ip_sock_set_tos$"; then
echo "INFO: ${msg} not supported by the kernel: SKIP"
return
fi
ip netns exec "$listener_ns" nft -f /dev/stdin <<"EOF"
flush ruleset
table inet mangle {
......@@ -767,6 +777,11 @@ run_tests_peekmode()
run_tests_mptfo()
{
if ! mptcp_lib_kallsyms_has "mptcp_fastopen_"; then
echo "INFO: TFO not supported by the kernel: SKIP"
return
fi
echo "INFO: with MPTFO start"
ip netns exec "$ns1" sysctl -q net.ipv4.tcp_fastopen=2
ip netns exec "$ns2" sysctl -q net.ipv4.tcp_fastopen=1
......@@ -787,6 +802,11 @@ run_tests_disconnect()
local old_cin=$cin
local old_sin=$sin
if ! mptcp_lib_kallsyms_has "mptcp_pm_data_reset$"; then
echo "INFO: Full disconnect not supported: SKIP"
return
fi
cat $cin $cin $cin > "$cin".disconnect
# force do_transfer to cope with the multiple tranmissions
......
......@@ -38,3 +38,41 @@ mptcp_lib_check_mptcp() {
exit ${KSFT_SKIP}
fi
}
mptcp_lib_check_kallsyms() {
if ! mptcp_lib_has_file "/proc/kallsyms"; then
echo "SKIP: CONFIG_KALLSYMS is missing"
exit ${KSFT_SKIP}
fi
}
# Internal: use mptcp_lib_kallsyms_has() instead
__mptcp_lib_kallsyms_has() {
local sym="${1}"
mptcp_lib_check_kallsyms
grep -q " ${sym}" /proc/kallsyms
}
# $1: part of a symbol to look at, add '$' at the end for full name
mptcp_lib_kallsyms_has() {
local sym="${1}"
if __mptcp_lib_kallsyms_has "${sym}"; then
return 0
fi
mptcp_lib_fail_if_expected_feature "${sym} symbol not found"
}
# $1: part of a symbol to look at, add '$' at the end for full name
mptcp_lib_kallsyms_doesnt_have() {
local sym="${1}"
if ! __mptcp_lib_kallsyms_has "${sym}"; then
return 0
fi
mptcp_lib_fail_if_expected_feature "${sym} symbol has been found"
}
......@@ -87,6 +87,10 @@ struct so_state {
uint64_t tcpi_rcv_delta;
};
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
static void die_perror(const char *msg)
{
perror(msg);
......@@ -349,13 +353,14 @@ static void do_getsockopt_tcp_info(struct so_state *s, int fd, size_t r, size_t
xerror("getsockopt MPTCP_TCPINFO (tries %d, %m)");
assert(olen <= sizeof(ti));
assert(ti.d.size_user == ti.d.size_kernel);
assert(ti.d.size_user == sizeof(struct tcp_info));
assert(ti.d.size_kernel > 0);
assert(ti.d.size_user ==
MIN(ti.d.size_kernel, sizeof(struct tcp_info)));
assert(ti.d.num_subflows == 1);
assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
olen -= sizeof(struct mptcp_subflow_data);
assert(olen == sizeof(struct tcp_info));
assert(olen == ti.d.size_user);
if (ti.ti[0].tcpi_bytes_sent == w &&
ti.ti[0].tcpi_bytes_received == r)
......@@ -401,13 +406,14 @@ static void do_getsockopt_subflow_addrs(int fd)
die_perror("getsockopt MPTCP_SUBFLOW_ADDRS");
assert(olen <= sizeof(addrs));
assert(addrs.d.size_user == addrs.d.size_kernel);
assert(addrs.d.size_user == sizeof(struct mptcp_subflow_addrs));
assert(addrs.d.size_kernel > 0);
assert(addrs.d.size_user ==
MIN(addrs.d.size_kernel, sizeof(struct mptcp_subflow_addrs)));
assert(addrs.d.num_subflows == 1);
assert(olen > (socklen_t)sizeof(struct mptcp_subflow_data));
olen -= sizeof(struct mptcp_subflow_data);
assert(olen == sizeof(struct mptcp_subflow_addrs));
assert(olen == addrs.d.size_user);
llen = sizeof(local);
ret = getsockname(fd, (struct sockaddr *)&local, &llen);
......
......@@ -87,6 +87,7 @@ cleanup()
}
mptcp_lib_check_mptcp
mptcp_lib_check_kallsyms
ip -Version > /dev/null 2>&1
if [ $? -ne 0 ];then
......@@ -186,9 +187,14 @@ do_transfer()
local_addr="0.0.0.0"
fi
cmsg="TIMESTAMPNS"
if mptcp_lib_kallsyms_has "mptcp_ioctl$"; then
cmsg+=",TCPINQ"
fi
timeout ${timeout_test} \
ip netns exec ${listener_ns} \
$mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c TIMESTAMPNS,TCPINQ \
$mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c "${cmsg}" \
${local_addr} < "$sin" > "$sout" &
local spid=$!
......@@ -196,7 +202,7 @@ do_transfer()
timeout ${timeout_test} \
ip netns exec ${connector_ns} \
$mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c TIMESTAMPNS,TCPINQ \
$mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c "${cmsg}" \
$connect_addr < "$cin" > "$cout" &
local cpid=$!
......@@ -253,6 +259,11 @@ do_mptcp_sockopt_tests()
{
local lret=0
if ! mptcp_lib_kallsyms_has "mptcp_diag_fill_info$"; then
echo "INFO: MPTCP sockopt not supported: SKIP"
return
fi
ip netns exec "$ns_sbox" ./mptcp_sockopt
lret=$?
......@@ -307,6 +318,11 @@ do_tcpinq_tests()
{
local lret=0
if ! mptcp_lib_kallsyms_has "mptcp_ioctl$"; then
echo "INFO: TCP_INQ not supported: SKIP"
return
fi
local args
for args in "-t tcp" "-r tcp"; do
do_tcpinq_test $args
......
......@@ -73,8 +73,12 @@ check()
}
check "ip netns exec $ns1 ./pm_nl_ctl dump" "" "defaults addr list"
check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0
default_limits="$(ip netns exec $ns1 ./pm_nl_ctl limits)"
if mptcp_lib_expect_all_features; then
check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0
subflows 2" "defaults limits"
fi
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.1
ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.2 flags subflow dev lo
......@@ -121,12 +125,10 @@ ip netns exec $ns1 ./pm_nl_ctl flush
check "ip netns exec $ns1 ./pm_nl_ctl dump" "" "flush addrs"
ip netns exec $ns1 ./pm_nl_ctl limits 9 1
check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0
subflows 2" "rcv addrs above hard limit"
check "ip netns exec $ns1 ./pm_nl_ctl limits" "$default_limits" "rcv addrs above hard limit"
ip netns exec $ns1 ./pm_nl_ctl limits 1 9
check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 0
subflows 2" "subflows above hard limit"
check "ip netns exec $ns1 ./pm_nl_ctl limits" "$default_limits" "subflows above hard limit"
ip netns exec $ns1 ./pm_nl_ctl limits 8 8
check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 8
......@@ -176,14 +178,19 @@ subflow,backup 10.0.1.1" "set flags (backup)"
ip netns exec $ns1 ./pm_nl_ctl set 10.0.1.1 flags nobackup
check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
subflow 10.0.1.1" " (nobackup)"
# fullmesh support has been added later
ip netns exec $ns1 ./pm_nl_ctl set id 1 flags fullmesh
check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
if ip netns exec $ns1 ./pm_nl_ctl dump | grep -q "fullmesh" ||
mptcp_lib_expect_all_features; then
check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
subflow,fullmesh 10.0.1.1" " (fullmesh)"
ip netns exec $ns1 ./pm_nl_ctl set id 1 flags nofullmesh
check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
ip netns exec $ns1 ./pm_nl_ctl set id 1 flags nofullmesh
check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
subflow 10.0.1.1" " (nofullmesh)"
ip netns exec $ns1 ./pm_nl_ctl set id 1 flags backup,fullmesh
check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
ip netns exec $ns1 ./pm_nl_ctl set id 1 flags backup,fullmesh
check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags \
subflow,backup,fullmesh 10.0.1.1" " (backup,fullmesh)"
fi
exit $ret
......@@ -4,11 +4,17 @@
. "$(dirname "${0}")/mptcp_lib.sh"
mptcp_lib_check_mptcp
mptcp_lib_check_kallsyms
if ! mptcp_lib_has_file '/proc/sys/net/mptcp/pm_type'; then
echo "userspace pm tests are not supported by the kernel: SKIP"
exit ${KSFT_SKIP}
fi
ip -Version > /dev/null 2>&1
if [ $? -ne 0 ];then
echo "SKIP: Cannot not run test without ip tool"
exit 1
exit ${KSFT_SKIP}
fi
ANNOUNCED=6 # MPTCP_EVENT_ANNOUNCED
......@@ -909,6 +915,11 @@ test_listener()
{
print_title "Listener tests"
if ! mptcp_lib_kallsyms_has "mptcp_event_pm_listener$"; then
stdbuf -o0 -e0 printf "LISTENER events \t[SKIP] Not supported\n"
return
fi
# Capture events on the network namespace running the client
:>$client_evts
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment