Add a kselftest that exercises the SMC getsockopt() paths converted to the getsockopt_iter() / sockopt_t callback: - SOL_SMC options (SMC_LIMIT_HS), handled directly by smc_getsockopt(), which returns the int value through copy_to_iter() and reports the written length in opt->optlen. - The CLC pass-through (e.g. SOL_TCP), where smc_getsockopt() forwards to the underlying TCP socket: optval is reconstructed from iter_out, the optlen pointer is forwarded, and the clamped length is mirrored back through opt->optlen. The oversized-buffer case (input optlen differs from output) specifically guards against a missing writeback sync. Signed-off-by: Breno Leitao --- tools/testing/selftests/net/Makefile | 1 + tools/testing/selftests/net/getsockopt_smc.c | 175 +++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 5ca6c557fc3f..5b50f718dbde 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -177,6 +177,7 @@ TEST_GEN_PROGS := \ bind_wildcard \ epoll_busy_poll \ getsockopt_iter \ + getsockopt_smc \ icmp_rfc4884 \ ipv6_fragmentation \ proc_net_pktgen \ diff --git a/tools/testing/selftests/net/getsockopt_smc.c b/tools/testing/selftests/net/getsockopt_smc.c new file mode 100644 index 000000000000..239deefb3187 --- /dev/null +++ b/tools/testing/selftests/net/getsockopt_smc.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Exercise the SMC getsockopt() paths that were converted to the + * getsockopt_iter() / sockopt_t callback. + * + * Two distinct paths are covered: + * + * - SOL_SMC options (SMC_LIMIT_HS) are handled directly by + * smc_getsockopt(), which returns the int value through copy_to_iter() + * and reports the written length in opt->optlen. + * + * - Other levels (e.g. SOL_TCP) are forwarded to the underlying CLC (TCP) + * socket, whose getsockopt() still operates on __user buffers. The + * converted smc_getsockopt() reconstructs the userspace optval from + * iter_out, forwards the original optlen pointer, and mirrors the length + * the clcsock reported back into opt->optlen so the core writes the right + * value to userspace. + * + * The kernel-buffer (kvec) path of the CLC pass-through returns -EOPNOTSUPP + * and is not reachable from a userspace getsockopt(), so it is not tested + * here. + * + * Author: Breno Leitao + */ +#include +#include +#include +#include +#include +#include +#include + +#include "kselftest_harness.h" + +#ifndef AF_SMC +#define AF_SMC 43 +#endif +#ifndef SMCPROTO_SMC +#define SMCPROTO_SMC 0 +#endif +#ifndef SOL_SMC +#define SOL_SMC 286 +#endif +#ifndef SMC_LIMIT_HS +#define SMC_LIMIT_HS 1 +#endif + +FIXTURE(smc) { + int fd; +}; + +FIXTURE_SETUP(smc) +{ + self->fd = socket(AF_SMC, SOCK_STREAM, SMCPROTO_SMC); + if (self->fd < 0) + SKIP(return, "AF_SMC unavailable (errno %d) - load the smc module", + errno); +} + +FIXTURE_TEARDOWN(smc) +{ + if (self->fd >= 0) + close(self->fd); +} + +/* ---------- SOL_SMC: handled directly by smc_getsockopt() ---------- */ + +/* SMC_LIMIT_HS is reported back as a 4-byte int via copy_to_iter(). */ +TEST_F(smc, limit_hs_default) +{ + socklen_t optlen = sizeof(int); + int val = 0xdeadbeef; + + ASSERT_EQ(0, getsockopt(self->fd, SOL_SMC, SMC_LIMIT_HS, &val, &optlen)); + EXPECT_EQ(sizeof(int), optlen); + EXPECT_TRUE(val == 0 || val == 1); +} + +/* A value set via setsockopt() must be readable back unchanged. */ +TEST_F(smc, limit_hs_set_get) +{ + socklen_t optlen = sizeof(int); + int val = 1; + + ASSERT_EQ(0, setsockopt(self->fd, SOL_SMC, SMC_LIMIT_HS, &val, optlen)); + + val = -1; + ASSERT_EQ(0, getsockopt(self->fd, SOL_SMC, SMC_LIMIT_HS, &val, &optlen)); + EXPECT_EQ(sizeof(int), optlen); + EXPECT_EQ(1, val); +} + +/* setsockopt() stores !!val, so a non-1 truthy value reads back as 1. */ +TEST_F(smc, limit_hs_set_get_clear) +{ + socklen_t optlen = sizeof(int); + int val = 0; + + ASSERT_EQ(0, setsockopt(self->fd, SOL_SMC, SMC_LIMIT_HS, &val, optlen)); + + val = -1; + ASSERT_EQ(0, getsockopt(self->fd, SOL_SMC, SMC_LIMIT_HS, &val, &optlen)); + EXPECT_EQ(sizeof(int), optlen); + EXPECT_EQ(0, val); +} + +/* An oversized buffer is clamped: optlen is reported back as sizeof(int). */ +TEST_F(smc, limit_hs_oversize_clamped) +{ + socklen_t optlen; + char buf[16] = {}; + + optlen = sizeof(buf); + ASSERT_EQ(0, getsockopt(self->fd, SOL_SMC, SMC_LIMIT_HS, buf, &optlen)); + EXPECT_EQ(sizeof(int), optlen); +} + +/* An unknown SOL_SMC option is rejected with -EOPNOTSUPP. */ +TEST_F(smc, bad_optname) +{ + socklen_t optlen = sizeof(int); + int val; + + ASSERT_EQ(-1, getsockopt(self->fd, SOL_SMC, 0x7fff, &val, &optlen)); + EXPECT_EQ(EOPNOTSUPP, errno); +} + +/* ---------- CLC pass-through: forwarded to the underlying TCP socket ------ */ + +/* A TCP option set on the SMC socket is applied to the CLC socket and must be + * readable back through the pass-through, exercising optval reconstruction. + */ +TEST_F(smc, clc_tcp_nodelay_set_get) +{ + socklen_t optlen = sizeof(int); + int val = 1; + + ASSERT_EQ(0, setsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY, + &val, optlen)); + + val = -1; + ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY, + &val, &optlen)); + EXPECT_EQ(sizeof(int), optlen); + EXPECT_EQ(1, val); +} + +/* With an oversized buffer the clcsock clamps the reported length to + * sizeof(int). That length is produced by the clcsock writing the user optlen + * pointer, and must be mirrored back through opt->optlen; since the input + * optlen (16) differs from the output (4), this fails if the writeback sync + * in smc_getsockopt() is missing. + */ +TEST_F(smc, clc_tcp_nodelay_oversize_clamped) +{ + socklen_t optlen; + char buf[16] = {}; + + optlen = sizeof(buf); + ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY, + buf, &optlen)); + EXPECT_EQ(sizeof(int), optlen); +} + +/* An error from the clcsock (unknown TCP option) is propagated unchanged. */ +TEST_F(smc, clc_bad_optname) +{ + socklen_t optlen = sizeof(int); + int val; + + ASSERT_EQ(-1, getsockopt(self->fd, IPPROTO_TCP, 0x7fff, &val, &optlen)); + EXPECT_EQ(ENOPROTOOPT, errno); +} + +TEST_HARNESS_MAIN -- 2.53.0-Meta