* [PATCH net] tcp: restore IPv6 af_ops on disconnect of a v4-mapped socket
@ 2026-10-30 3:23 Zhiyu Zhang
2026-10-30 3:29 ` netdev-bot+sinfo
0 siblings, 1 reply; 2+ messages in thread
From: Zhiyu Zhang @ 2026-10-30 3:23 UTC (permalink / raw)
To: Eric Dumazet, Kuniyuki Iwashima
Cc: Neal Cardwell, David S. Miller, Jakub Kicinski, Paolo Abeni,
Simon Horman, Matthieu Baerts, Mat Martineau, Geliang Tang,
netdev, mptcp, linux-kernel, Tong Liu, Qi Tang
From: Eric Dumazet <edumazet@kernel.org>
A successful connect() of an IPv6 socket to a v4-mapped address switches
icsk_af_ops, sk_backlog_rcv and af_specific to their IPv4-mapped variants.
These are only restored if the connect() fails.
After connect(AF_UNSPEC), the socket can be reused for listen() or for a
native IPv6 connect while still using the mapped handlers. IPv6 packets
processed from the socket backlog are then handled by tcp_v4_do_rcv().
tcp_v4_send_reset() and tcp_v4_send_ack() interpret
TCP_SKB_CB(skb)->header.h6 as IPv4 options, and __ip_options_echo() can
overflow its 40-byte destination buffer.
Commit 8cc3aef0cb19 ("tcp: Do not allow buggy transitions between ehash
and lhash2.") prevents listen() after connect(AF_UNSPEC), but a native IPv6
connect is still allowed.
Add tcp_v6_disconnect() to restore the IPv6 handlers when a v4-mapped
socket is disconnected, and use the same helper in the connect error path.
MPTCP subflows override sk_prot, and two teardown paths call
tcp_disconnect() directly. Route those calls through the protocol-specific
disconnect operation so IPv6 MPTCP subflows receive the same restoration.
Tested with TCP and MPTCP by reconnecting an AF_INET6 socket from a
v4-mapped peer to a native IPv6 peer after connect(AF_UNSPEC).
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: Tong Liu <lyutoon@gmail.com>
Reported-by: Qi Tang <tpluszz77@gmail.com>
Reported-by: Zhiyu Zhang <zhiyuzhang999@gmail.com>
Closes: https://lore.kernel.org/netdev/CALf2hKvp0NPVLwr=a3+jUHtPohxbra8gox+aYAfWathJnn-J+Q@mail.gmail.com/
Link: https://lore.kernel.org/netdev/CAL4WiiqFJpmcV4=TWRpukCq6q=-729658FwGvieNcPXe97kLvQ@mail.gmail.com/
Link: https://lore.kernel.org/lkml/20261029054136.1174216-1-zhiyuzhang999@gmail.com/
Signed-off-by: Eric Dumazet <edumazet@kernel.org>
Co-developed-by: Zhiyu Zhang <zhiyuzhang999@gmail.com>
Signed-off-by: Zhiyu Zhang <zhiyuzhang999@gmail.com>
---
net/ipv6/tcp_ipv6.c | 37 ++++++++++++++++++++++++++++---------
net/mptcp/protocol.c | 4 ++--
2 files changed, 30 insertions(+), 11 deletions(-)
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index e006a30360ea..a93410aab66e 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -130,6 +130,19 @@ static int tcp_v6_pre_connect(struct sock *sk, struct sockaddr_unsized *uaddr,
return BPF_CGROUP_RUN_PROG_INET6_CONNECT(sk, uaddr, &addr_len);
}
+/* Undo the v4-mapped setup done by tcp_v6_connect(). */
+static void tcp_v6_restore_af_ops(struct sock *sk)
+{
+ /* Paired with READ_ONCE() in tcp_(get|set)sockopt() */
+ WRITE_ONCE(inet_csk(sk)->icsk_af_ops, &ipv6_specific);
+ if (sk_is_mptcp(sk))
+ mptcpv6_handle_mapped(sk, false);
+ sk->sk_backlog_rcv = tcp_v6_do_rcv;
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
+ tcp_sk(sk)->af_specific = &tcp_sock_ipv6_specific;
+#endif
+}
+
static int tcp_v6_connect(struct sock *sk, struct sockaddr_unsized *uaddr,
int addr_len)
{
@@ -243,14 +256,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr_unsized *uaddr,
if (err) {
icsk->icsk_ext_hdr_len = exthdrlen;
- /* Paired with READ_ONCE() in tcp_(get|set)sockopt() */
- WRITE_ONCE(icsk->icsk_af_ops, &ipv6_specific);
- if (sk_is_mptcp(sk))
- mptcpv6_handle_mapped(sk, false);
- sk->sk_backlog_rcv = tcp_v6_do_rcv;
-#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
- tp->af_specific = &tcp_sock_ipv6_specific;
-#endif
+ tcp_v6_restore_af_ops(sk);
goto failure;
}
np->saddr = sk->sk_v6_rcv_saddr;
@@ -2093,6 +2099,19 @@ static int tcp_v6_init_sock(struct sock *sk)
return 0;
}
+static int tcp_v6_disconnect(struct sock *sk, int flags)
+{
+ int ret = tcp_disconnect(sk, flags);
+
+ /* A socket used for a v4-mapped connection must not keep IPv4
+ * handlers when it is reused for listen() or a native IPv6 connect().
+ */
+ if (sk->sk_backlog_rcv == tcp_v4_do_rcv)
+ tcp_v6_restore_af_ops(sk);
+
+ return ret;
+}
+
#ifdef CONFIG_PROC_FS
/* Proc filesystem TCPv6 sock list dumping. */
static void get_openreq6(struct seq_file *seq,
@@ -2283,7 +2302,7 @@ struct proto tcpv6_prot = {
.close = tcp_close,
.pre_connect = tcp_v6_pre_connect,
.connect = tcp_v6_connect,
- .disconnect = tcp_disconnect,
+ .disconnect = tcp_v6_disconnect,
.accept = inet_csk_accept,
.ioctl = tcp_ioctl,
.init = tcp_v6_init_sock,
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 7a22e9388e0e..fdfbc7de2ec0 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -2608,7 +2608,7 @@ static void __mptcp_subflow_disconnect(struct sock *ssk,
/* The MPTCP code never wait on the subflow sockets, TCP-level
* disconnect should never fail
*/
- WARN_ON_ONCE(tcp_disconnect(ssk, 0));
+ WARN_ON_ONCE(ssk->sk_prot->disconnect(ssk, 0));
mptcp_subflow_ctx_reset(subflow);
} else {
tcp_shutdown(ssk, SEND_SHUTDOWN);
@@ -3287,7 +3287,7 @@ void mptcp_subflow_shutdown(struct sock *sk, struct sock *ssk, int how)
break;
fallthrough;
case TCP_SYN_SENT:
- WARN_ON_ONCE(tcp_disconnect(ssk, O_NONBLOCK));
+ WARN_ON_ONCE(ssk->sk_prot->disconnect(ssk, O_NONBLOCK));
break;
default:
if (__mptcp_check_fallback(mptcp_sk(sk))) {
base-commit: 5838f10563d58afca6711edb5e4eda968ca181d9
--
2.34.1
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH net] tcp: restore IPv6 af_ops on disconnect of a v4-mapped socket
2026-10-30 3:23 [PATCH net] tcp: restore IPv6 af_ops on disconnect of a v4-mapped socket Zhiyu Zhang
@ 2026-10-30 3:29 ` netdev-bot+sinfo
0 siblings, 0 replies; 2+ messages in thread
From: netdev-bot+sinfo @ 2026-10-30 3:29 UTC (permalink / raw)
To: Zhiyu Zhang
Cc: Eric Dumazet, Kuniyuki Iwashima, Neal Cardwell, David S. Miller,
Jakub Kicinski, Paolo Abeni, Simon Horman, Matthieu Baerts,
Mat Martineau, Geliang Tang, netdev, mptcp, linux-kernel,
Tong Liu, Qi Tang
Hi!
This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:
- How the issue was discovered, e.g. hit in production, hit during
development, syzbot report, manual code inspection, LLM or static
analysis tool scan.
Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.
The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-30 3:29 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-30 3:23 [PATCH net] tcp: restore IPv6 af_ops on disconnect of a v4-mapped socket Zhiyu Zhang
2026-10-30 3:29 ` netdev-bot+sinfo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox