* [PATCH net-next 1/7] ipv6: raw: drop unused level argument from do_rawv6_getsockopt
2026-07-16 12:59 [PATCH net-next 0/7] net: convert rawv6, ieee802154, phonet and tls getsockopt to sockopt_t Breno Leitao
@ 2026-07-16 12:59 ` Breno Leitao
2026-07-16 13:00 ` [PATCH net-next 2/7] ipv6: raw: convert do_rawv6_getsockopt to sockopt_t Breno Leitao
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Breno Leitao @ 2026-07-16 12:59 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team
do_rawv6_getsockopt() takes a level argument but never uses it; the
level dispatch is handled by the caller, rawv6_getsockopt(). Drop it,
matching ipv4's do_raw_getsockopt().
No functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/ipv6/raw.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 3cc58698cbbd3..99eec36796fb9 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -1051,8 +1051,8 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname,
return do_rawv6_setsockopt(sk, level, optname, optval, optlen);
}
-static int do_rawv6_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen)
+static int do_rawv6_getsockopt(struct sock *sk, int optname,
+ char __user *optval, int __user *optlen)
{
struct raw6_sock *rp = raw6_sk(sk);
int val, len;
@@ -1109,7 +1109,7 @@ static int rawv6_getsockopt(struct sock *sk, int level, int optname,
return ipv6_getsockopt(sk, level, optname, optval, optlen);
}
- return do_rawv6_getsockopt(sk, level, optname, optval, optlen);
+ return do_rawv6_getsockopt(sk, optname, optval, optlen);
}
static int rawv6_ioctl(struct sock *sk, int cmd, int *karg)
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH net-next 2/7] ipv6: raw: convert do_rawv6_getsockopt to sockopt_t
2026-07-16 12:59 [PATCH net-next 0/7] net: convert rawv6, ieee802154, phonet and tls getsockopt to sockopt_t Breno Leitao
2026-07-16 12:59 ` [PATCH net-next 1/7] ipv6: raw: drop unused level argument from do_rawv6_getsockopt Breno Leitao
@ 2026-07-16 13:00 ` Breno Leitao
2026-07-16 13:00 ` [PATCH net-next 3/7] ieee802154: convert dgram getsockopt " Breno Leitao
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Breno Leitao @ 2026-07-16 13:00 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team
Convert do_rawv6_getsockopt to the new sockopt_t model, mirroring what
we have in ipv4. The overall goal is to move these callbacks gradually
from __user points to use sockopt_t, and this part touches
do_rawv6_getsockopt.
No functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/ipv6/raw.c | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 99eec36796fb9..1f15942d14163 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -1051,14 +1051,12 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname,
return do_rawv6_setsockopt(sk, level, optname, optval, optlen);
}
-static int do_rawv6_getsockopt(struct sock *sk, int optname,
- char __user *optval, int __user *optlen)
+static int do_rawv6_getsockopt(struct sock *sk, int optname, sockopt_t *opt)
{
struct raw6_sock *rp = raw6_sk(sk);
int val, len;
- if (get_user(len, optlen))
- return -EFAULT;
+ len = opt->optlen;
switch (optname) {
case IPV6_HDRINCL:
@@ -1082,9 +1080,8 @@ static int do_rawv6_getsockopt(struct sock *sk, int optname,
len = min_t(unsigned int, sizeof(int), len);
- if (put_user(len, optlen))
- return -EFAULT;
- if (copy_to_user(optval, &val, len))
+ opt->optlen = len;
+ if (copy_to_iter(&val, len, &opt->iter_out) != len)
return -EFAULT;
return 0;
}
@@ -1092,6 +1089,9 @@ static int do_rawv6_getsockopt(struct sock *sk, int optname,
static int rawv6_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
+ sockopt_t opt;
+ int err;
+
switch (level) {
case SOL_RAW:
break;
@@ -1109,7 +1109,18 @@ static int rawv6_getsockopt(struct sock *sk, int level, int optname,
return ipv6_getsockopt(sk, level, optname, optval, optlen);
}
- return do_rawv6_getsockopt(sk, optname, optval, optlen);
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = do_rawv6_getsockopt(sk, optname, &opt);
+ if (err)
+ return err;
+
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+
+ return 0;
}
static int rawv6_ioctl(struct sock *sk, int cmd, int *karg)
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH net-next 3/7] ieee802154: convert dgram getsockopt to sockopt_t
2026-07-16 12:59 [PATCH net-next 0/7] net: convert rawv6, ieee802154, phonet and tls getsockopt to sockopt_t Breno Leitao
2026-07-16 12:59 ` [PATCH net-next 1/7] ipv6: raw: drop unused level argument from do_rawv6_getsockopt Breno Leitao
2026-07-16 13:00 ` [PATCH net-next 2/7] ipv6: raw: convert do_rawv6_getsockopt to sockopt_t Breno Leitao
@ 2026-07-16 13:00 ` Breno Leitao
2026-07-16 13:00 ` [PATCH net-next 4/7] phonet: pep: do not write beyond optlen in getsockopt Breno Leitao
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Breno Leitao @ 2026-07-16 13:00 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team
Continue converting the proto-layer getsockopt callbacks to the sockopt_t
interface, splitting dgram_getsockopt() into a do_dgram_getsockopt() helper
that takes a sockopt_t.
No functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/ieee802154/socket.c | 38 ++++++++++++++++++++++++++------------
1 file changed, 26 insertions(+), 12 deletions(-)
diff --git a/net/ieee802154/socket.c b/net/ieee802154/socket.c
index 85dce296d7513..763f63e48afe4 100644
--- a/net/ieee802154/socket.c
+++ b/net/ieee802154/socket.c
@@ -831,20 +831,12 @@ static int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb)
return ret;
}
-static int dgram_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen)
+static int do_dgram_getsockopt(struct sock *sk, int optname, sockopt_t *opt)
{
struct dgram_sock *ro = dgram_sk(sk);
-
int val, len;
- if (level != SOL_IEEE802154)
- return -EOPNOTSUPP;
-
- if (get_user(len, optlen))
- return -EFAULT;
-
- len = min_t(unsigned int, len, sizeof(int));
+ len = min_t(unsigned int, opt->optlen, sizeof(int));
switch (optname) {
case WPAN_WANTACK:
@@ -871,10 +863,32 @@ static int dgram_getsockopt(struct sock *sk, int level, int optname,
return -ENOPROTOOPT;
}
- if (put_user(len, optlen))
+ opt->optlen = len;
+ if (copy_to_iter(&val, len, &opt->iter_out) != len)
return -EFAULT;
- if (copy_to_user(optval, &val, len))
+ return 0;
+}
+
+static int dgram_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ sockopt_t opt;
+ int err;
+
+ if (level != SOL_IEEE802154)
+ return -EOPNOTSUPP;
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = do_dgram_getsockopt(sk, optname, &opt);
+ if (err)
+ return err;
+
+ if (put_user(opt.optlen, optlen))
return -EFAULT;
+
return 0;
}
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH net-next 4/7] phonet: pep: do not write beyond optlen in getsockopt
2026-07-16 12:59 [PATCH net-next 0/7] net: convert rawv6, ieee802154, phonet and tls getsockopt to sockopt_t Breno Leitao
` (2 preceding siblings ...)
2026-07-16 13:00 ` [PATCH net-next 3/7] ieee802154: convert dgram getsockopt " Breno Leitao
@ 2026-07-16 13:00 ` Breno Leitao
2026-07-16 13:00 ` [PATCH net-next 5/7] phonet: pep: convert getsockopt to sockopt_t Breno Leitao
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Breno Leitao @ 2026-07-16 13:00 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team
pep_getsockopt() clamps the reported length to the caller's buffer with
min_t(), but then stores the value with put_user(val, (int __user *)
optval), which always writes sizeof(int) bytes. A getsockopt() call with
an optlen smaller than sizeof(int) thus reports the clamped length yet
writes a full int, one to three bytes past the user buffer.
Write the value with copy_to_user() bounded by len, so at most optlen
bytes are copied, matching the length reported back to userspace.
Fixes: 02a47617cdce ("Phonet: implement GPRS virtual interface over PEP socket")
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/phonet/pep.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
index 7069271393933..60d1a5375725b 100644
--- a/net/phonet/pep.c
+++ b/net/phonet/pep.c
@@ -1115,7 +1115,7 @@ static int pep_getsockopt(struct sock *sk, int level, int optname,
len = min_t(unsigned int, sizeof(int), len);
if (put_user(len, optlen))
return -EFAULT;
- if (put_user(val, (int __user *) optval))
+ if (copy_to_user(optval, &val, len))
return -EFAULT;
return 0;
}
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH net-next 5/7] phonet: pep: convert getsockopt to sockopt_t
2026-07-16 12:59 [PATCH net-next 0/7] net: convert rawv6, ieee802154, phonet and tls getsockopt to sockopt_t Breno Leitao
` (3 preceding siblings ...)
2026-07-16 13:00 ` [PATCH net-next 4/7] phonet: pep: do not write beyond optlen in getsockopt Breno Leitao
@ 2026-07-16 13:00 ` Breno Leitao
2026-07-16 13:00 ` [PATCH net-next 6/7] tls: " Breno Leitao
2026-07-16 13:00 ` [PATCH net-next 7/7] selftests: net: getsockopt_iter: cover rawv6, ieee802154, phonet and tls Breno Leitao
6 siblings, 0 replies; 8+ messages in thread
From: Breno Leitao @ 2026-07-16 13:00 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team
Continue converting the proto-layer getsockopt callbacks to the
sockopt_t interface, splitting pep_getsockopt() into a
do_pep_getsockopt() helper that takes a sockopt_t.
The thin pep_getsockopt() wrapper keeps its __user signature for now:
it builds a user-backed sockopt_t with sockopt_init_user(), calls the
helper, and writes the returned length back to optlen. The helper uses
copy_to_iter() instead of copy_to_user(). No functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/phonet/pep.c | 36 ++++++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 10 deletions(-)
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
index 60d1a5375725b..c7f4ce894af56 100644
--- a/net/phonet/pep.c
+++ b/net/phonet/pep.c
@@ -1078,17 +1078,11 @@ static int pep_setsockopt(struct sock *sk, int level, int optname,
return err;
}
-static int pep_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen)
+static int do_pep_getsockopt(struct sock *sk, int optname, sockopt_t *opt)
{
struct pep_sock *pn = pep_sk(sk);
int len, val;
- if (level != SOL_PNPIPE)
- return -ENOPROTOOPT;
- if (get_user(len, optlen))
- return -EFAULT;
-
switch (optname) {
case PNPIPE_ENCAP:
val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE;
@@ -1112,11 +1106,33 @@ static int pep_getsockopt(struct sock *sk, int level, int optname,
return -ENOPROTOOPT;
}
- len = min_t(unsigned int, sizeof(int), len);
- if (put_user(len, optlen))
+ len = min_t(unsigned int, sizeof(int), opt->optlen);
+ opt->optlen = len;
+ if (copy_to_iter(&val, len, &opt->iter_out) != len)
return -EFAULT;
- if (copy_to_user(optval, &val, len))
+ return 0;
+}
+
+static int pep_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ sockopt_t opt;
+ int err;
+
+ if (level != SOL_PNPIPE)
+ return -ENOPROTOOPT;
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = do_pep_getsockopt(sk, optname, &opt);
+ if (err)
+ return err;
+
+ if (put_user(opt.optlen, optlen))
return -EFAULT;
+
return 0;
}
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH net-next 6/7] tls: convert getsockopt to sockopt_t
2026-07-16 12:59 [PATCH net-next 0/7] net: convert rawv6, ieee802154, phonet and tls getsockopt to sockopt_t Breno Leitao
` (4 preceding siblings ...)
2026-07-16 13:00 ` [PATCH net-next 5/7] phonet: pep: convert getsockopt to sockopt_t Breno Leitao
@ 2026-07-16 13:00 ` Breno Leitao
2026-07-16 13:00 ` [PATCH net-next 7/7] selftests: net: getsockopt_iter: cover rawv6, ieee802154, phonet and tls Breno Leitao
6 siblings, 0 replies; 8+ messages in thread
From: Breno Leitao @ 2026-07-16 13:00 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team
Continue converting the proto-layer getsockopt callbacks to the sockopt_t
interface, converting do_tls_getsockopt() and its per-option helpers to
take a sockopt_t.
The thin tls_getsockopt() wrapper keeps its __user signature for now: it
builds a user-backed sockopt_t with sockopt_init_user(), calls the helper,
and writes the returned length back to optlen. The helpers use
copy_to_iter() instead of copy_to_user(); the NULL optval check in the
TLS_TX/TLS_RX path is preserved by testing the iterator user buffer.
No functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
net/tls/tls_main.c | 80 ++++++++++++++++++++++++++----------------------------
1 file changed, 38 insertions(+), 42 deletions(-)
diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
index 8c588cdab733d..fbb274287aa5f 100644
--- a/net/tls/tls_main.c
+++ b/net/tls/tls_main.c
@@ -424,20 +424,16 @@ static __poll_t tls_sk_poll(struct file *file, struct socket *sock,
return mask;
}
-static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
- int __user *optlen, int tx)
+static int do_tls_getsockopt_conf(struct sock *sk, sockopt_t *opt, int tx)
{
int rc = 0;
const struct tls_cipher_desc *cipher_desc;
struct tls_context *ctx = tls_get_ctx(sk);
struct tls_crypto_info *crypto_info;
struct cipher_context *cctx;
- int len;
+ int len = opt->optlen;
- if (get_user(len, optlen))
- return -EFAULT;
-
- if (!optval || (len < sizeof(*crypto_info))) {
+ if (!opt->iter_out.ubuf || len < sizeof(*crypto_info)) {
rc = -EINVAL;
goto out;
}
@@ -462,7 +458,8 @@ static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
}
if (len == sizeof(*crypto_info)) {
- if (copy_to_user(optval, crypto_info, sizeof(*crypto_info)))
+ if (copy_to_iter(crypto_info, sizeof(*crypto_info),
+ &opt->iter_out) != sizeof(*crypto_info))
rc = -EFAULT;
goto out;
}
@@ -478,44 +475,38 @@ static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
memcpy(crypto_info_rec_seq(crypto_info, cipher_desc),
cctx->rec_seq, cipher_desc->rec_seq);
- if (copy_to_user(optval, crypto_info, cipher_desc->crypto_info))
+ if (copy_to_iter(crypto_info, cipher_desc->crypto_info,
+ &opt->iter_out) != cipher_desc->crypto_info)
rc = -EFAULT;
out:
return rc;
}
-static int do_tls_getsockopt_tx_zc(struct sock *sk, char __user *optval,
- int __user *optlen)
+static int do_tls_getsockopt_tx_zc(struct sock *sk, sockopt_t *opt)
{
struct tls_context *ctx = tls_get_ctx(sk);
unsigned int value;
- int len;
-
- if (get_user(len, optlen))
- return -EFAULT;
+ int len = opt->optlen;
if (len != sizeof(value))
return -EINVAL;
value = ctx->zerocopy_sendfile;
- if (copy_to_user(optval, &value, sizeof(value)))
+ if (copy_to_iter(&value, sizeof(value), &opt->iter_out) != sizeof(value))
return -EFAULT;
return 0;
}
-static int do_tls_getsockopt_no_pad(struct sock *sk, char __user *optval,
- int __user *optlen)
+static int do_tls_getsockopt_no_pad(struct sock *sk, sockopt_t *opt)
{
struct tls_context *ctx = tls_get_ctx(sk);
- int value, len;
+ int value, len = opt->optlen;
if (ctx->prot_info.version != TLS_1_3_VERSION)
return -EINVAL;
- if (get_user(len, optlen))
- return -EFAULT;
if (len < sizeof(value))
return -EINVAL;
@@ -525,38 +516,31 @@ static int do_tls_getsockopt_no_pad(struct sock *sk, char __user *optval,
if (value < 0)
return value;
- if (put_user(sizeof(value), optlen))
- return -EFAULT;
- if (copy_to_user(optval, &value, sizeof(value)))
+ opt->optlen = sizeof(value);
+ if (copy_to_iter(&value, sizeof(value), &opt->iter_out) != sizeof(value))
return -EFAULT;
return 0;
}
-static int do_tls_getsockopt_tx_payload_len(struct sock *sk, char __user *optval,
- int __user *optlen)
+static int do_tls_getsockopt_tx_payload_len(struct sock *sk, sockopt_t *opt)
{
struct tls_context *ctx = tls_get_ctx(sk);
u16 payload_len = ctx->tx_max_payload_len;
- int len;
-
- if (get_user(len, optlen))
- return -EFAULT;
+ int len = opt->optlen;
if (len < sizeof(payload_len))
return -EINVAL;
- if (put_user(sizeof(payload_len), optlen))
- return -EFAULT;
-
- if (copy_to_user(optval, &payload_len, sizeof(payload_len)))
+ opt->optlen = sizeof(payload_len);
+ if (copy_to_iter(&payload_len, sizeof(payload_len),
+ &opt->iter_out) != sizeof(payload_len))
return -EFAULT;
return 0;
}
-static int do_tls_getsockopt(struct sock *sk, int optname,
- char __user *optval, int __user *optlen)
+static int do_tls_getsockopt(struct sock *sk, int optname, sockopt_t *opt)
{
int rc = 0;
@@ -565,17 +549,16 @@ static int do_tls_getsockopt(struct sock *sk, int optname,
switch (optname) {
case TLS_TX:
case TLS_RX:
- rc = do_tls_getsockopt_conf(sk, optval, optlen,
- optname == TLS_TX);
+ rc = do_tls_getsockopt_conf(sk, opt, optname == TLS_TX);
break;
case TLS_TX_ZEROCOPY_RO:
- rc = do_tls_getsockopt_tx_zc(sk, optval, optlen);
+ rc = do_tls_getsockopt_tx_zc(sk, opt);
break;
case TLS_RX_EXPECT_NO_PAD:
- rc = do_tls_getsockopt_no_pad(sk, optval, optlen);
+ rc = do_tls_getsockopt_no_pad(sk, opt);
break;
case TLS_TX_MAX_PAYLOAD_LEN:
- rc = do_tls_getsockopt_tx_payload_len(sk, optval, optlen);
+ rc = do_tls_getsockopt_tx_payload_len(sk, opt);
break;
default:
rc = -ENOPROTOOPT;
@@ -591,12 +574,25 @@ static int tls_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
struct tls_context *ctx = tls_get_ctx(sk);
+ sockopt_t opt;
+ int err;
if (level != SOL_TLS)
return ctx->sk_proto->getsockopt(sk, level,
optname, optval, optlen);
- return do_tls_getsockopt(sk, optname, optval, optlen);
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = do_tls_getsockopt(sk, optname, &opt);
+ if (err)
+ return err;
+
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+
+ return 0;
}
static int validate_crypto_info(const struct tls_crypto_info *crypto_info,
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH net-next 7/7] selftests: net: getsockopt_iter: cover rawv6, ieee802154, phonet and tls
2026-07-16 12:59 [PATCH net-next 0/7] net: convert rawv6, ieee802154, phonet and tls getsockopt to sockopt_t Breno Leitao
` (5 preceding siblings ...)
2026-07-16 13:00 ` [PATCH net-next 6/7] tls: " Breno Leitao
@ 2026-07-16 13:00 ` Breno Leitao
6 siblings, 0 replies; 8+ messages in thread
From: Breno Leitao @ 2026-07-16 13:00 UTC (permalink / raw)
To: sdf, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Alexander Aring, Stefan Schmidt, Miquel Raynal,
Remi Denis-Courmont, Rémi Denis-Courmont, John Fastabend,
Sabrina Dubroca, Shuah Khan
Cc: netdev, linux-kernel, linux-wpan, linux-kselftest, Breno Leitao,
kernel-team
Add fixtures for the newly converted getsockopt leaves:
- rawv6: IPV6_HDRINCL / IPV6_CHECKSUM int paths + a SOL_RAW
unknown-optname case that reaches do_rawv6_getsockopt().
- ieee802154: WPAN_WANTACK dgram int path + non-SOL_IEEE802154 level
rejection.
- phonet: PNPIPE_ENCAP pep int path + non-SOL_PNPIPE level
rejection.
- tls: TLS_TX_ZEROCOPY_RO, the TLS_TX crypto_info round-trip at
the base and full cipher sizes, the NULL-optval and short
buffer EINVAL paths, and an unknown optname. It skips when
the kernel lacks TLS or AES-GCM.
Each fixture pins the returned-length / errno semantics across exact,
oversized and short buffers, an unknown optname and a bogus level. The
semantics are unchanged by the sockopt_t conversion, so the tests pass
both before and after the leaf conversions.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
tools/testing/selftests/net/getsockopt_iter.c | 424 ++++++++++++++++++++++++++
1 file changed, 424 insertions(+)
diff --git a/tools/testing/selftests/net/getsockopt_iter.c b/tools/testing/selftests/net/getsockopt_iter.c
index fe5a5268bc34e..974065a23fa82 100644
--- a/tools/testing/selftests/net/getsockopt_iter.c
+++ b/tools/testing/selftests/net/getsockopt_iter.c
@@ -28,7 +28,10 @@
#include <linux/vm_sockets.h>
#include <linux/icmp.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
#include <sys/socket.h>
+#include <linux/tls.h>
#include "kselftest_harness.h"
#ifndef AF_VSOCK
@@ -40,6 +43,45 @@
#ifndef ICMP_FILTER
#define ICMP_FILTER 1
#endif
+#ifndef IPV6_HDRINCL
+#define IPV6_HDRINCL 36
+#endif
+#ifndef IPV6_CHECKSUM
+#define IPV6_CHECKSUM 7
+#endif
+#ifndef AF_IEEE802154
+#define AF_IEEE802154 36
+#endif
+#ifndef SOL_IEEE802154
+#define SOL_IEEE802154 0
+#endif
+#ifndef WPAN_WANTACK
+#define WPAN_WANTACK 0
+#endif
+#ifndef AF_PHONET
+#define AF_PHONET 35
+#endif
+#ifndef SOL_PNPIPE
+#define SOL_PNPIPE 275
+#endif
+#ifndef PN_PROTO_PIPE
+#define PN_PROTO_PIPE 2
+#endif
+#ifndef PNPIPE_ENCAP
+#define PNPIPE_ENCAP 1
+#endif
+#ifndef PNPIPE_ENCAP_NONE
+#define PNPIPE_ENCAP_NONE 0
+#endif
+#ifndef PNPIPE_ENCAP_IP
+#define PNPIPE_ENCAP_IP 1
+#endif
+#ifndef SOL_TLS
+#define SOL_TLS 282
+#endif
+#ifndef TCP_ULP
+#define TCP_ULP 31
+#endif
/* ---------- netlink ---------- */
@@ -394,4 +436,386 @@ TEST_F(raw, bad_optname)
ASSERT_EQ(sizeof(val), optlen);
}
+/* ---------- raw (ipv6) ---------- */
+
+FIXTURE(rawv6)
+{
+ int fd;
+};
+
+FIXTURE_SETUP(rawv6)
+{
+ self->fd = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
+ if (self->fd < 0)
+ SKIP(return, "SOCK_RAW/IPv6 socket: %s", strerror(errno));
+}
+
+FIXTURE_TEARDOWN(rawv6)
+{
+ if (self->fd >= 0)
+ close(self->fd);
+}
+
+TEST_F(rawv6, hdrincl_exact)
+{
+ socklen_t optlen;
+ int val = -1;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_HDRINCL,
+ &val, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+ ASSERT_TRUE(val == 0 || val == 1);
+}
+
+TEST_F(rawv6, hdrincl_oversize_clamped)
+{
+ char buf[16] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_HDRINCL,
+ buf, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+}
+
+/* Raw int options clamp the reported length down to the user buffer
+ * instead of returning EINVAL on a short buffer.
+ */
+TEST_F(rawv6, hdrincl_undersize_clamped)
+{
+ socklen_t optlen = 2;
+ int val = 0;
+
+ ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_HDRINCL,
+ &val, &optlen));
+ ASSERT_EQ(2, optlen);
+}
+
+TEST_F(rawv6, checksum_default)
+{
+ socklen_t optlen;
+ int val = 0;
+
+ optlen = sizeof(val);
+
+ /* A non-ICMPv6 raw socket has the checksum disabled, reported as -1. */
+ ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_CHECKSUM,
+ &val, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+ ASSERT_EQ(-1, val);
+}
+
+TEST_F(rawv6, bad_optname)
+{
+ socklen_t optlen;
+ int val;
+
+ optlen = sizeof(val);
+
+ /* SOL_RAW reaches do_rawv6_getsockopt() directly. */
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_RAW, 0x7fff, &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+ ASSERT_EQ(sizeof(val), optlen);
+}
+
+/* ---------- ieee802154 (dgram) ---------- */
+
+FIXTURE(ieee802154)
+{
+ int fd;
+};
+
+FIXTURE_SETUP(ieee802154)
+{
+ self->fd = socket(AF_IEEE802154, SOCK_DGRAM, 0);
+ if (self->fd < 0)
+ SKIP(return, "AF_IEEE802154 dgram socket: %s", strerror(errno));
+}
+
+FIXTURE_TEARDOWN(ieee802154)
+{
+ if (self->fd >= 0)
+ close(self->fd);
+}
+
+TEST_F(ieee802154, wantack_exact)
+{
+ socklen_t optlen;
+ int val = -1;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IEEE802154, WPAN_WANTACK,
+ &val, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+ ASSERT_TRUE(val == 0 || val == 1);
+}
+
+TEST_F(ieee802154, wantack_oversize_clamped)
+{
+ char buf[16] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IEEE802154, WPAN_WANTACK,
+ buf, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+}
+
+TEST_F(ieee802154, wantack_undersize_clamped)
+{
+ socklen_t optlen = 2;
+ int val = 0;
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_IEEE802154, WPAN_WANTACK,
+ &val, &optlen));
+ ASSERT_EQ(2, optlen);
+}
+
+TEST_F(ieee802154, bad_optname)
+{
+ socklen_t optlen;
+ int val;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_IEEE802154, 0x7fff,
+ &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+ ASSERT_EQ(sizeof(val), optlen);
+}
+
+/* dgram_getsockopt() rejects any level other than SOL_IEEE802154. */
+TEST_F(ieee802154, bad_level)
+{
+ socklen_t optlen;
+ int val;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_RAW, WPAN_WANTACK,
+ &val, &optlen));
+ ASSERT_EQ(EOPNOTSUPP, errno);
+ ASSERT_EQ(sizeof(val), optlen);
+}
+
+/* ---------- phonet (pep) ---------- */
+
+FIXTURE(phonet)
+{
+ int fd;
+};
+
+FIXTURE_SETUP(phonet)
+{
+ self->fd = socket(AF_PHONET, SOCK_SEQPACKET, PN_PROTO_PIPE);
+ if (self->fd < 0)
+ SKIP(return, "AF_PHONET pipe socket: %s", strerror(errno));
+}
+
+FIXTURE_TEARDOWN(phonet)
+{
+ if (self->fd >= 0)
+ close(self->fd);
+}
+
+TEST_F(phonet, encap_exact)
+{
+ socklen_t optlen;
+ int val = -1;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_PNPIPE, PNPIPE_ENCAP,
+ &val, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+ ASSERT_TRUE(val == PNPIPE_ENCAP_NONE || val == PNPIPE_ENCAP_IP);
+}
+
+TEST_F(phonet, encap_oversize_clamped)
+{
+ char buf[16] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_PNPIPE, PNPIPE_ENCAP,
+ buf, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+}
+
+/* pep clamps the reported length down to the user buffer. Use an
+ * int-sized backing buffer with a short optlen so the baseline kernel,
+ * which writes a full int via put_user(), does not scribble past it.
+ */
+TEST_F(phonet, encap_undersize_clamped)
+{
+ socklen_t optlen = 2;
+ int val = 0;
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_PNPIPE, PNPIPE_ENCAP,
+ &val, &optlen));
+ ASSERT_EQ(2, optlen);
+}
+
+TEST_F(phonet, bad_optname)
+{
+ socklen_t optlen;
+ int val;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_PNPIPE, 0x7fff, &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+ ASSERT_EQ(sizeof(val), optlen);
+}
+
+/* pep_getsockopt() rejects any level other than SOL_PNPIPE. */
+TEST_F(phonet, bad_level)
+{
+ socklen_t optlen;
+ int val;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_RAW, PNPIPE_ENCAP, &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+ ASSERT_EQ(sizeof(val), optlen);
+}
+
+/* ---------- tls ---------- */
+
+FIXTURE(tls)
+{
+ int fd;
+ int sfd;
+};
+
+FIXTURE_SETUP(tls)
+{
+ struct sockaddr_in a = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ };
+ socklen_t alen = sizeof(a);
+ int lfd;
+
+ self->fd = -1;
+ self->sfd = -1;
+
+ lfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (lfd < 0)
+ SKIP(return, "TCP socket: %s", strerror(errno));
+ if (bind(lfd, (struct sockaddr *)&a, sizeof(a)) || listen(lfd, 1) ||
+ getsockname(lfd, (struct sockaddr *)&a, &alen)) {
+ close(lfd);
+ SKIP(return, "listener setup: %s", strerror(errno));
+ }
+ self->fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (connect(self->fd, (struct sockaddr *)&a, sizeof(a))) {
+ close(lfd);
+ SKIP(return, "connect: %s", strerror(errno));
+ }
+ self->sfd = accept(lfd, NULL, NULL);
+ close(lfd);
+ if (setsockopt(self->fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls")))
+ SKIP(return, "TCP_ULP=tls: %s (built without TLS?)",
+ strerror(errno));
+}
+
+FIXTURE_TEARDOWN(tls)
+{
+ if (self->fd >= 0)
+ close(self->fd);
+ if (self->sfd >= 0)
+ close(self->sfd);
+}
+
+/* do_tls_getsockopt_tx_zc(): fixed-size int, exact length required. */
+TEST_F(tls, tx_zerocopy_exact)
+{
+ socklen_t optlen = sizeof(int);
+ int val = -1;
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_TLS, TLS_TX_ZEROCOPY_RO,
+ &val, &optlen));
+ ASSERT_EQ(sizeof(int), optlen);
+ ASSERT_TRUE(val == 0 || val == 1);
+}
+
+TEST_F(tls, tx_zerocopy_wrong_len)
+{
+ socklen_t optlen = 2;
+ int val;
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX_ZEROCOPY_RO,
+ &val, &optlen));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+/* do_tls_getsockopt_conf(): NULL optval still yields EINVAL -- the
+ * converted code tests opt->iter_out.ubuf in place of optval.
+ */
+TEST_F(tls, conf_null_optval)
+{
+ socklen_t optlen = 64;
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX, NULL, &optlen));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+TEST_F(tls, conf_short)
+{
+ socklen_t optlen = 2;
+ char buf[2];
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX, buf, &optlen));
+ ASSERT_EQ(EINVAL, errno);
+}
+
+/* TLS_TX before crypto is set reports not-ready. */
+TEST_F(tls, conf_not_ready)
+{
+ struct tls_crypto_info info;
+ socklen_t optlen = sizeof(info);
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX, &info, &optlen));
+ ASSERT_EQ(EBUSY, errno);
+}
+
+/* Set TX crypto, then read it back at the base and full sizes, exercising
+ * both copy_to_iter() branches. SKIP if AES-GCM is unavailable.
+ */
+TEST_F(tls, conf_crypto_roundtrip)
+{
+ struct tls12_crypto_info_aes_gcm_128 tx = {
+ .info.version = TLS_1_2_VERSION,
+ .info.cipher_type = TLS_CIPHER_AES_GCM_128,
+ };
+ struct tls12_crypto_info_aes_gcm_128 full;
+ struct tls_crypto_info base;
+ socklen_t optlen;
+
+ if (setsockopt(self->fd, SOL_TLS, TLS_TX, &tx, sizeof(tx)))
+ SKIP(return, "set TLS_TX aes_gcm_128: %s", strerror(errno));
+
+ optlen = sizeof(base);
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_TLS, TLS_TX, &base, &optlen));
+ ASSERT_EQ(sizeof(base), optlen);
+ ASSERT_EQ(TLS_1_2_VERSION, base.version);
+ ASSERT_EQ(TLS_CIPHER_AES_GCM_128, base.cipher_type);
+
+ optlen = sizeof(full);
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_TLS, TLS_TX, &full, &optlen));
+ ASSERT_EQ(sizeof(full), optlen);
+ ASSERT_EQ(TLS_CIPHER_AES_GCM_128, full.info.cipher_type);
+}
+
+TEST_F(tls, bad_optname)
+{
+ socklen_t optlen = sizeof(int);
+ int val;
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, 0x7fff, &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+}
+
TEST_HARNESS_MAIN
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 8+ messages in thread