From: Breno Leitao <leitao@debian.org>
To: David Ahern <dsahern@kernel.org>,
Ido Schimmel <idosch@nvidia.com>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>,
Paolo Abeni <pabeni@redhat.com>, Simon Horman <horms@kernel.org>,
Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Eduard Zingerman <eddyz87@gmail.com>,
Kumar Kartikeya Dwivedi <memxor@gmail.com>,
Martin KaFai Lau <martin.lau@linux.dev>,
Song Liu <song@kernel.org>,
Yonghong Song <yonghong.song@linux.dev>,
Jiri Olsa <jolsa@kernel.org>,
Emil Tsalapatis <emil@etsalapatis.com>,
Ihor Solodrai <ihor.solodrai@linux.dev>,
John Fastabend <john.fastabend@gmail.com>,
Stanislav Fomichev <sdf@fomichev.me>,
Shuah Khan <shuah@kernel.org>
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
bpf@vger.kernel.org, linux-kselftest@vger.kernel.org,
david.laight.linux@gmail.com, Breno Leitao <leitao@debian.org>,
kernel-team@meta.com
Subject: [PATCH net-next 6/6] selftests: net: getsockopt_iter: cover ip and ipv6
Date: Fri, 25 Sep 2026 08:55:21 -0700 [thread overview]
Message-ID: <20260925-sockopt_expand_out_v2-v1-6-c3ef2e3bb5c0@debian.org> (raw)
In-Reply-To: <20260925-sockopt_expand_out_v2-v1-0-c3ef2e3bb5c0@debian.org>
Add an ip and an ipv6 fixture, pinning the returned length and errno
across buffer sizes, the branches that answer empty, an unknown
optname and the options dispatched before the switch.
SOL_IP answers a sub-int buffer with one byte where SOL_IPV6 clamps the
int. IP_PKTOPTIONS and IPV6_2292PKTOPTIONS want a stream socket;
MRT_*/MRT6_* want a raw one, and skip without CONFIG_IP_MROUTE or
CONFIG_IPV6_MROUTE, where the switch answers ENOPROTOOPT instead.
MRT_VERSION and MRT6_VERSION are spelled out; linux/mroute.h does not
coexist with netinet/in.h here.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
tools/testing/selftests/net/getsockopt_iter.c | 302 ++++++++++++++++++++++++++
1 file changed, 302 insertions(+)
diff --git a/tools/testing/selftests/net/getsockopt_iter.c b/tools/testing/selftests/net/getsockopt_iter.c
index 6c2408df461232..32b6e0a50f1f9c 100644
--- a/tools/testing/selftests/net/getsockopt_iter.c
+++ b/tools/testing/selftests/net/getsockopt_iter.c
@@ -55,6 +55,13 @@
#ifndef TCP_ULP
#define TCP_ULP 31
#endif
+/* linux/mroute.h does not coexist with netinet/in.h here. */
+#ifndef MRT_VERSION
+#define MRT_VERSION 206
+#endif
+#ifndef MRT6_VERSION
+#define MRT6_VERSION 206
+#endif
/* ---------- netlink ---------- */
@@ -492,6 +499,301 @@ TEST_F(rawv6, bad_optname)
ASSERT_EQ(sizeof(val), optlen);
}
+/* ---------- ip (SOL_IP) ---------- */
+
+FIXTURE(ip)
+{
+ int fd;
+};
+
+FIXTURE_SETUP(ip)
+{
+ /* a router alert option, so IP_OPTIONS has something to answer with */
+ static const unsigned char ipopts[4] = { 0x94, 0x04, 0x00, 0x00 };
+ int ttl = 42;
+
+ self->fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (self->fd < 0)
+ SKIP(return, "AF_INET dgram socket: %s", strerror(errno));
+
+ if (setsockopt(self->fd, SOL_IP, IP_TTL, &ttl, sizeof(ttl)) < 0)
+ SKIP(return, "set IP_TTL: %s", strerror(errno));
+
+ if (setsockopt(self->fd, SOL_IP, IP_OPTIONS, ipopts,
+ sizeof(ipopts)) < 0)
+ SKIP(return, "set IP_OPTIONS: %s", strerror(errno));
+}
+
+FIXTURE_TEARDOWN(ip)
+{
+ if (self->fd >= 0)
+ close(self->fd);
+}
+
+TEST_F(ip, ttl_exact)
+{
+ socklen_t optlen = sizeof(int);
+ int val = 0;
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IP, IP_TTL, &val, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+ ASSERT_EQ(42, val);
+}
+
+TEST_F(ip, ttl_oversize_clamped)
+{
+ socklen_t optlen = 64;
+ char buf[64] = {};
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IP, IP_TTL, buf, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+}
+
+/* SOL_IP answers a sub-int buffer with a single byte when the value fits
+ * in one, rather than clamping the int down.
+ */
+TEST_F(ip, ttl_single_byte)
+{
+ unsigned char buf[3] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IP, IP_TTL, buf, &optlen));
+ ASSERT_EQ(1, optlen);
+ ASSERT_EQ(42, buf[0]);
+}
+
+TEST_F(ip, ttl_zero_len)
+{
+ socklen_t optlen = 0;
+ int val;
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IP, IP_TTL, &val, &optlen));
+ ASSERT_EQ(0, optlen);
+}
+
+TEST_F(ip, negative_optlen)
+{
+ socklen_t optlen = (socklen_t)-1;
+ int val;
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_IP, IP_TTL, &val, &optlen));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+TEST_F(ip, options_roundtrip)
+{
+ unsigned char buf[40] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IP, IP_OPTIONS, buf, &optlen));
+ ASSERT_EQ(4, optlen);
+ ASSERT_EQ(0x94, buf[0]);
+}
+
+TEST_F(ip, options_undersize_clamped)
+{
+ unsigned char buf[2] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IP, IP_OPTIONS, buf, &optlen));
+ ASSERT_EQ(sizeof(buf), optlen);
+}
+
+/* With no option set the reply is empty and the call still succeeds. */
+TEST_F(ip, options_absent)
+{
+ unsigned char buf[40] = {};
+ socklen_t optlen = sizeof(buf);
+ int fd;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ SKIP(return, "AF_INET dgram socket: %s", strerror(errno));
+
+ ASSERT_EQ(0, getsockopt(fd, SOL_IP, IP_OPTIONS, buf, &optlen));
+ ASSERT_EQ(0, optlen);
+ close(fd);
+}
+
+TEST_F(ip, multicast_if_oversize_clamped)
+{
+ socklen_t optlen = 64;
+ char buf[64] = {};
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IP, IP_MULTICAST_IF, buf,
+ &optlen));
+ ASSERT_EQ(sizeof(struct in_addr), optlen);
+}
+
+/* IP_PKTOPTIONS only answers on a stream socket. */
+TEST_F(ip, pktoptions_wrong_type)
+{
+ socklen_t optlen = 64;
+ char buf[64];
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_IP, IP_PKTOPTIONS, buf,
+ &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+}
+
+/* The MRT_* options are dispatched ahead of the rest of the switch and
+ * want a raw IGMP socket. Without CONFIG_IP_MROUTE they are not
+ * dispatched at all and the switch answers ENOPROTOOPT instead.
+ */
+TEST_F(ip, mroute_wrong_type)
+{
+ socklen_t optlen = sizeof(int);
+ int val;
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_IP, MRT_VERSION, &val,
+ &optlen));
+ if (errno == ENOPROTOOPT)
+ SKIP(return, "CONFIG_IP_MROUTE disabled");
+ ASSERT_EQ(EOPNOTSUPP, errno);
+}
+
+TEST_F(ip, bad_optname)
+{
+ socklen_t optlen = sizeof(int);
+ int val;
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_IP, 0x7fff, &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+ ASSERT_EQ(sizeof(int), optlen);
+}
+
+/* ---------- ipv6 (SOL_IPV6) ---------- */
+
+FIXTURE(ipv6)
+{
+ int fd;
+};
+
+FIXTURE_SETUP(ipv6)
+{
+ /* an 8 byte hop-by-hop header, so the sticky options answer */
+ static const unsigned char hopopt[8] = { 0, 0, 1, 4, 0, 0, 0, 0 };
+ int hops = 42;
+
+ self->fd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (self->fd < 0)
+ SKIP(return, "AF_INET6 dgram socket: %s", strerror(errno));
+
+ if (setsockopt(self->fd, SOL_IPV6, IPV6_UNICAST_HOPS, &hops,
+ sizeof(hops)) < 0)
+ SKIP(return, "set IPV6_UNICAST_HOPS: %s", strerror(errno));
+
+ if (setsockopt(self->fd, SOL_IPV6, IPV6_HOPOPTS, hopopt,
+ sizeof(hopopt)) < 0)
+ SKIP(return, "set IPV6_HOPOPTS: %s", strerror(errno));
+}
+
+FIXTURE_TEARDOWN(ipv6)
+{
+ if (self->fd >= 0)
+ close(self->fd);
+}
+
+TEST_F(ipv6, hops_exact)
+{
+ socklen_t optlen = sizeof(int);
+ int val = 0;
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IPV6, IPV6_UNICAST_HOPS, &val,
+ &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+ ASSERT_EQ(42, val);
+}
+
+TEST_F(ipv6, hops_oversize_clamped)
+{
+ socklen_t optlen = 64;
+ char buf[64] = {};
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IPV6, IPV6_UNICAST_HOPS, buf,
+ &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+}
+
+TEST_F(ipv6, hopopts_roundtrip)
+{
+ unsigned char buf[64] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IPV6, IPV6_HOPOPTS, buf,
+ &optlen));
+ ASSERT_EQ(8, optlen);
+}
+
+TEST_F(ipv6, hopopts_undersize_clamped)
+{
+ unsigned char buf[4] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IPV6, IPV6_HOPOPTS, buf,
+ &optlen));
+ ASSERT_EQ(sizeof(buf), optlen);
+}
+
+/* With no header set the reply is empty and the call still succeeds. */
+TEST_F(ipv6, hopopts_absent)
+{
+ unsigned char buf[64] = {};
+ socklen_t optlen = sizeof(buf);
+ int fd;
+
+ fd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (fd < 0)
+ SKIP(return, "AF_INET6 dgram socket: %s", strerror(errno));
+
+ ASSERT_EQ(0, getsockopt(fd, SOL_IPV6, IPV6_HOPOPTS, buf, &optlen));
+ ASSERT_EQ(0, optlen);
+ close(fd);
+}
+
+/* IPV6_PATHMTU wants room for the whole struct ip6_mtuinfo. */
+TEST_F(ipv6, pathmtu_undersize)
+{
+ socklen_t optlen = 8;
+ char buf[8];
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_IPV6, IPV6_PATHMTU, buf,
+ &optlen));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+TEST_F(ipv6, pktoptions_wrong_type)
+{
+ socklen_t optlen = 64;
+ char buf[64];
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_IPV6, IPV6_2292PKTOPTIONS, buf,
+ &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+}
+
+TEST_F(ipv6, mroute_wrong_type)
+{
+ socklen_t optlen = sizeof(int);
+ int val;
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_IPV6, MRT6_VERSION, &val,
+ &optlen));
+ if (errno == ENOPROTOOPT)
+ SKIP(return, "CONFIG_IPV6_MROUTE disabled");
+ ASSERT_EQ(EOPNOTSUPP, errno);
+}
+
+TEST_F(ipv6, bad_optname)
+{
+ socklen_t optlen = sizeof(int);
+ int val;
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_IPV6, 0x7fff, &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+ ASSERT_EQ(sizeof(int), optlen);
+}
+
/* ---------- tls ---------- */
FIXTURE(tls)
--
2.53.0-Meta
next prev parent reply other threads:[~2026-09-25 15:56 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-25 15:55 [PATCH net-next 0/6] ipv4,ipv6: convert the getsockopt switches to sockopt_t Breno Leitao
2026-09-25 15:55 ` [PATCH net-next 1/6] ipv6: reject a negative optlen in do_ipv6_getsockopt() Breno Leitao
2026-09-25 19:02 ` Stanislav Fomichev
2026-09-27 6:53 ` David Laight
2026-09-29 12:14 ` Breno Leitao
2026-09-28 18:55 ` netdev-bot+sashiko
2026-09-25 15:55 ` [PATCH net-next 2/6] ipv6: mcast: convert ip6_mc_msfget() to sockopt_t Breno Leitao
2026-09-28 18:55 ` netdev-bot+sashiko
2026-09-25 15:55 ` [PATCH net-next 3/6] ipv4: igmp: convert ip_mc_gsfget() " Breno Leitao
2026-09-28 18:55 ` netdev-bot+sashiko
2026-09-25 15:55 ` [PATCH net-next 4/6] ipv4: convert do_ip_getsockopt() " Breno Leitao
2026-09-28 18:55 ` netdev-bot+sashiko
2026-09-25 15:55 ` [PATCH net-next 5/6] ipv6: convert do_ipv6_getsockopt() " Breno Leitao
2026-09-28 18:55 ` netdev-bot+sashiko
2026-09-25 15:55 ` Breno Leitao [this message]
2026-09-28 18:55 ` [PATCH net-next 6/6] selftests: net: getsockopt_iter: cover ip and ipv6 netdev-bot+sashiko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260925-sockopt_expand_out_v2-v1-6-c3ef2e3bb5c0@debian.org \
--to=leitao@debian.org \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=davem@davemloft.net \
--cc=david.laight.linux@gmail.com \
--cc=dsahern@kernel.org \
--cc=eddyz87@gmail.com \
--cc=edumazet@google.com \
--cc=emil@etsalapatis.com \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=ihor.solodrai@linux.dev \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=kernel-team@meta.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=memxor@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=sdf@fomichev.me \
--cc=shuah@kernel.org \
--cc=song@kernel.org \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox