On Fri, May 01, 2026 at 08:52:53AM -0700, Breno Leitao wrote: > Add a single kselftest covering the proto_ops getsockopt_iter > conversions for AF_NETLINK and AF_VSOCK, using one fixture per protocol: > > netlink: > > NETLINK_PKTINFO covers the flag-style int path (exact size, oversize > clamp, undersize -EINVAL); NETLINK_LIST_MEMBERSHIPS covers the > size-discovery path that always reports the required buffer length back > via optlen, even when the user buffer is too small to receive any group > bits. > > vsock: > SO_VM_SOCKETS_BUFFER_SIZE covers the u64 path (exact size, oversize > clamp, undersize -EINVAL). > > Each fixture also exercises an unknown optname and a bogus level so > the returned-length / errno semantics preserved by the sockopt_t > conversion are pinned down. > > Signed-off-by: Breno Leitao <[email protected]>
This all looks good to me. My only thought was SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW/OLD would be a nice add-on for testing greater than u64, but probably overkill since you already have NETLINK_LIST_MEMBERSHIPS. Code all looks reasonable. Reviewed-by: Bobby Eshleman <[email protected]> > --- > tools/testing/selftests/net/Makefile | 1 + > tools/testing/selftests/net/getsockopt_iter.c | 213 > ++++++++++++++++++++++++++ > 2 files changed, 214 insertions(+) > > diff --git a/tools/testing/selftests/net/Makefile > b/tools/testing/selftests/net/Makefile > index a275ed5840265..baa30287cf222 100644 > --- a/tools/testing/selftests/net/Makefile > +++ b/tools/testing/selftests/net/Makefile > @@ -176,6 +176,7 @@ TEST_GEN_PROGS := \ > bind_timewait \ > bind_wildcard \ > epoll_busy_poll \ > + getsockopt_iter \ > icmp_rfc4884 \ > ipv6_fragmentation \ > proc_net_pktgen \ > diff --git a/tools/testing/selftests/net/getsockopt_iter.c > b/tools/testing/selftests/net/getsockopt_iter.c > new file mode 100644 > index 0000000000000..179f9e84926fd > --- /dev/null > +++ b/tools/testing/selftests/net/getsockopt_iter.c > @@ -0,0 +1,213 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Quick test for getsockopt{_iter} tests. > + * > + * Each fixture targets one converted protocol and pins down the > + * returned-length / errno semantics across buffer-size variations, > + * an unknown optname and a bogus level. > + * > + * - netlink: NETLINK_PKTINFO covers the flag-style int path; the > + * NETLINK_LIST_MEMBERSHIPS cases cover the size-discovery path > + * that always reports the required buffer length back via optlen, > + * even when the user buffer is too small to receive any group bits. > + * - vsock: SO_VM_SOCKETS_BUFFER_SIZE covers the u64 path. > + * > + * Author: Breno Leitao <[email protected]> > + */ > + > +#include <errno.h> > +#include <stdint.h> > +#include <stdio.h> > +#include <string.h> > +#include <unistd.h> > +#include <linux/netlink.h> > +#include <linux/rtnetlink.h> > +#include <linux/vm_sockets.h> > +#include <sys/socket.h> > +#include "kselftest_harness.h" > + > +#ifndef AF_VSOCK > +#define AF_VSOCK 40 > +#endif > + > +/* ---------- netlink ---------- */ > + > +FIXTURE(netlink) > +{ > + int fd; > +}; > + > +FIXTURE_SETUP(netlink) > +{ > + int group = RTNLGRP_LINK; > + > + self->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); > + if (self->fd < 0) > + SKIP(return, "AF_NETLINK socket: %s", strerror(errno)); > + > + /* Joining a multicast group grows nlk->ngroups so the > + * NETLINK_LIST_MEMBERSHIPS path has a non-zero size to report. > + */ > + if (setsockopt(self->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, > + &group, sizeof(group)) < 0) > + SKIP(return, "NETLINK_ADD_MEMBERSHIP: %s", strerror(errno)); > +} > + > +FIXTURE_TEARDOWN(netlink) > +{ > + if (self->fd >= 0) > + close(self->fd); > +} > + > +TEST_F(netlink, pktinfo_exact) > +{ > + int val = -1; > + socklen_t optlen = sizeof(val); > + > + ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO, > + &val, &optlen)); > + ASSERT_EQ(sizeof(int), optlen); > + ASSERT_TRUE(val == 0 || val == 1); > +} > + > +TEST_F(netlink, pktinfo_oversize_clamped) > +{ > + char buf[16] = {}; > + socklen_t optlen = sizeof(buf); > + > + ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO, > + buf, &optlen)); > + ASSERT_EQ(sizeof(int), optlen); > +} > + > +TEST_F(netlink, pktinfo_undersize) > +{ > + char buf[2] = {}; > + socklen_t optlen = sizeof(buf); > + > + ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO, > + buf, &optlen)); > + ASSERT_EQ(EINVAL, errno); > +} > + > +TEST_F(netlink, list_memberships_size_discovery) > +{ > + socklen_t optlen = 0; > + char dummy; > + > + ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, > + NETLINK_LIST_MEMBERSHIPS, > + &dummy, &optlen)); > + ASSERT_GT(optlen, 0); > + ASSERT_EQ(0, optlen % sizeof(__u32)); > +} > + > +TEST_F(netlink, list_memberships_full_read) > +{ > + __u32 buf[64] = {}; > + socklen_t optlen = sizeof(buf); > + > + ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, > + NETLINK_LIST_MEMBERSHIPS, > + buf, &optlen)); > + ASSERT_GT(optlen, 0); > + ASSERT_LE(optlen, sizeof(buf)); > + ASSERT_EQ(0, optlen % sizeof(__u32)); > +} > + > +TEST_F(netlink, bad_level) > +{ > + int val; > + socklen_t optlen = sizeof(val); > + > + ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1, NETLINK_PKTINFO, > + &val, &optlen)); > + ASSERT_EQ(ENOPROTOOPT, errno); > +} > + > +TEST_F(netlink, bad_optname) > +{ > + int val; > + socklen_t optlen = sizeof(val); > + > + ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, 0x7fff, > + &val, &optlen)); > + ASSERT_EQ(ENOPROTOOPT, errno); > +} > + > +/* ---------- vsock ---------- */ > + > +FIXTURE(vsock) > +{ > + int fd; > +}; > + > +FIXTURE_SETUP(vsock) > +{ > + self->fd = socket(AF_VSOCK, SOCK_STREAM, 0); > + if (self->fd < 0) > + SKIP(return, "AF_VSOCK socket: %s", strerror(errno)); > +} > + > +FIXTURE_TEARDOWN(vsock) > +{ > + if (self->fd >= 0) > + close(self->fd); > +} > + > +TEST_F(vsock, buffer_size_exact) > +{ > + uint64_t val = 0; > + socklen_t optlen = sizeof(val); > + > + ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK, > + SO_VM_SOCKETS_BUFFER_SIZE, > + &val, &optlen)); > + ASSERT_EQ(sizeof(uint64_t), optlen); > + ASSERT_GT(val, 0); > +} > + > +TEST_F(vsock, buffer_size_oversize_clamped) > +{ > + char buf[16] = {}; > + socklen_t optlen = sizeof(buf); > + > + ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK, > + SO_VM_SOCKETS_BUFFER_SIZE, > + buf, &optlen)); > + ASSERT_EQ(sizeof(uint64_t), optlen); > +} > + > +TEST_F(vsock, buffer_size_undersize) > +{ > + char buf[4] = {}; > + socklen_t optlen = sizeof(buf); > + > + ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK, > + SO_VM_SOCKETS_BUFFER_SIZE, > + buf, &optlen)); > + ASSERT_EQ(EINVAL, errno); > +} > + > +TEST_F(vsock, bad_level) > +{ > + uint64_t val; > + socklen_t optlen = sizeof(val); > + > + ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1, > + SO_VM_SOCKETS_BUFFER_SIZE, > + &val, &optlen)); > + ASSERT_EQ(ENOPROTOOPT, errno); > +} > + > +TEST_F(vsock, bad_optname) > +{ > + uint64_t val; > + socklen_t optlen = sizeof(val); > + > + ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK, 0x7fff, > + &val, &optlen)); > + ASSERT_EQ(ENOPROTOOPT, errno); > +} > + > +TEST_HARNESS_MAIN > > -- > 2.52.0 >

