mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
ipv6: Remove IPV6_ADDRFORM.
Recently, IPV6_ADDRFORM has received many AI-driven bug reports.
Fixing them properly would needlessly churn the fast paths in TCP
and UDP.
IPV6_ADDRFORM was initially introduced in RFC 2133 in 1997,
but only two years later, it was removed from RFC 2553 in 1999.
In 2026, modern applications natively support dual-stack sockets;
notably, systemd's socket activation does not use IPV6_ADDRFORM.
Also, getsockopt(IPV6_ADDRFORM) can be replaced with SO_DOMAIN.
Let's remove IPV6_ADDRFORM.
Later, we can remove sk->sk_prot_creator and revert commit
c26c192c3d ("udp: properly deal with xfrm encap and ADDRFORM").
Reported-by: Daehyeon Ko <4ncienth@gmail.com>
Closes: https://lore.kernel.org/netdev/20260902010408.1057857-1-4ncienth@gmail.com/
Reported-by: Hyunwoo Kim <imv4bel@gmail.com>
Closes: https://lore.kernel.org/netdev/20260824033331.1084971-1-imv4bel@gmail.com/
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20260904033543.2635540-3-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
8cc3aef0cb
commit
31c5ea8a32
|
|
@ -166,7 +166,7 @@ struct socket {
|
||||||
|
|
||||||
struct file *file;
|
struct file *file;
|
||||||
struct sock *sk;
|
struct sock *sk;
|
||||||
const struct proto_ops *ops; /* Might change with IPV6_ADDRFORM or MPTCP. */
|
const struct proto_ops *ops; /* Might change with MPTCP. */
|
||||||
|
|
||||||
struct socket_wq wq;
|
struct socket_wq wq;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -770,7 +770,7 @@ bool sk_mc_loop(const struct sock *sk)
|
||||||
return false;
|
return false;
|
||||||
if (!sk)
|
if (!sk)
|
||||||
return true;
|
return true;
|
||||||
/* IPV6_ADDRFORM can change sk->sk_family under us. */
|
|
||||||
switch (READ_ONCE(sk->sk_family)) {
|
switch (READ_ONCE(sk->sk_family)) {
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
return inet_test_bit(MC_LOOP, sk);
|
return inet_test_bit(MC_LOOP, sk);
|
||||||
|
|
@ -4010,7 +4010,6 @@ int sock_common_getsockopt(struct socket *sock, int level, int optname,
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
|
|
||||||
/* IPV6_ADDRFORM can change sk->sk_prot under us. */
|
|
||||||
return READ_ONCE(sk->sk_prot)->getsockopt(sk, level, optname, optval, optlen);
|
return READ_ONCE(sk->sk_prot)->getsockopt(sk, level, optname, optval, optlen);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sock_common_getsockopt);
|
EXPORT_SYMBOL(sock_common_getsockopt);
|
||||||
|
|
@ -4032,7 +4031,6 @@ int sock_common_setsockopt(struct socket *sock, int level, int optname,
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
|
|
||||||
/* IPV6_ADDRFORM can change sk->sk_prot under us. */
|
|
||||||
return READ_ONCE(sk->sk_prot)->setsockopt(sk, level, optname, optval, optlen);
|
return READ_ONCE(sk->sk_prot)->setsockopt(sk, level, optname, optval, optlen);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sock_common_setsockopt);
|
EXPORT_SYMBOL(sock_common_setsockopt);
|
||||||
|
|
|
||||||
|
|
@ -582,7 +582,6 @@ int inet_dgram_connect(struct socket *sock, struct sockaddr_unsized *uaddr,
|
||||||
if (addr_len < sizeof(uaddr->sa_family))
|
if (addr_len < sizeof(uaddr->sa_family))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* IPV6_ADDRFORM can change sk->sk_prot under us. */
|
|
||||||
prot = READ_ONCE(sk->sk_prot);
|
prot = READ_ONCE(sk->sk_prot);
|
||||||
|
|
||||||
if (uaddr->sa_family == AF_UNSPEC)
|
if (uaddr->sa_family == AF_UNSPEC)
|
||||||
|
|
@ -789,7 +788,6 @@ int inet_accept(struct socket *sock, struct socket *newsock,
|
||||||
{
|
{
|
||||||
struct sock *sk1 = sock->sk, *sk2;
|
struct sock *sk1 = sock->sk, *sk2;
|
||||||
|
|
||||||
/* IPV6_ADDRFORM can change sk->sk_prot under us. */
|
|
||||||
arg->err = -EINVAL;
|
arg->err = -EINVAL;
|
||||||
sk2 = READ_ONCE(sk1->sk_prot)->accept(sk1, arg);
|
sk2 = READ_ONCE(sk1->sk_prot)->accept(sk1, arg);
|
||||||
if (!sk2)
|
if (!sk2)
|
||||||
|
|
@ -875,7 +873,6 @@ void inet_splice_eof(struct socket *sock)
|
||||||
if (unlikely(inet_send_prepare(sk)))
|
if (unlikely(inet_send_prepare(sk)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* IPV6_ADDRFORM can change sk->sk_prot under us. */
|
|
||||||
prot = READ_ONCE(sk->sk_prot);
|
prot = READ_ONCE(sk->sk_prot);
|
||||||
if (prot->splice_eof)
|
if (prot->splice_eof)
|
||||||
prot->splice_eof(sock);
|
prot->splice_eof(sock);
|
||||||
|
|
|
||||||
|
|
@ -429,7 +429,6 @@ int inet6_bind_sk(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len)
|
||||||
const struct proto *prot;
|
const struct proto *prot;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
/* IPV6_ADDRFORM can change sk->sk_prot under us. */
|
|
||||||
prot = READ_ONCE(sk->sk_prot);
|
prot = READ_ONCE(sk->sk_prot);
|
||||||
/* If the socket has its own bind function then use it. */
|
/* If the socket has its own bind function then use it. */
|
||||||
if (prot->bind)
|
if (prot->bind)
|
||||||
|
|
@ -567,7 +566,6 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
|
||||||
case SIOCSIFDSTADDR:
|
case SIOCSIFDSTADDR:
|
||||||
return addrconf_set_dstaddr(net, argp);
|
return addrconf_set_dstaddr(net, argp);
|
||||||
default:
|
default:
|
||||||
/* IPV6_ADDRFORM can change sk->sk_prot under us. */
|
|
||||||
prot = READ_ONCE(sk->sk_prot);
|
prot = READ_ONCE(sk->sk_prot);
|
||||||
if (!prot->ioctl)
|
if (!prot->ioctl)
|
||||||
return -ENOIOCTLCMD;
|
return -ENOIOCTLCMD;
|
||||||
|
|
@ -636,7 +634,6 @@ int inet6_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
|
||||||
if (unlikely(inet_send_prepare(sk)))
|
if (unlikely(inet_send_prepare(sk)))
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
/* IPV6_ADDRFORM can change sk->sk_prot under us. */
|
|
||||||
prot = READ_ONCE(sk->sk_prot);
|
prot = READ_ONCE(sk->sk_prot);
|
||||||
return INDIRECT_CALL_2(prot->sendmsg, tcp_sendmsg, udpv6_sendmsg,
|
return INDIRECT_CALL_2(prot->sendmsg, tcp_sendmsg, udpv6_sendmsg,
|
||||||
sk, msg, size);
|
sk, msg, size);
|
||||||
|
|
@ -651,7 +648,6 @@ int inet6_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
|
||||||
if (likely(!(flags & MSG_ERRQUEUE)))
|
if (likely(!(flags & MSG_ERRQUEUE)))
|
||||||
sock_rps_record_flow(sk);
|
sock_rps_record_flow(sk);
|
||||||
|
|
||||||
/* IPV6_ADDRFORM can change sk->sk_prot under us. */
|
|
||||||
prot = READ_ONCE(sk->sk_prot);
|
prot = READ_ONCE(sk->sk_prot);
|
||||||
return INDIRECT_CALL_2(prot->recvmsg, tcp_recvmsg, udpv6_recvmsg,
|
return INDIRECT_CALL_2(prot->recvmsg, tcp_recvmsg, udpv6_recvmsg,
|
||||||
sk, msg, size, flags);
|
sk, msg, size, flags);
|
||||||
|
|
|
||||||
|
|
@ -547,86 +547,7 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
||||||
|
|
||||||
sockopt_lock_sock(sk);
|
sockopt_lock_sock(sk);
|
||||||
|
|
||||||
/* Another thread has converted the socket into IPv4 with
|
|
||||||
* IPV6_ADDRFORM concurrently.
|
|
||||||
*/
|
|
||||||
if (unlikely(sk->sk_family != AF_INET6))
|
|
||||||
goto unlock;
|
|
||||||
|
|
||||||
switch (optname) {
|
switch (optname) {
|
||||||
|
|
||||||
case IPV6_ADDRFORM:
|
|
||||||
if (optlen < sizeof(int))
|
|
||||||
goto e_inval;
|
|
||||||
if (val == PF_INET) {
|
|
||||||
if (sk->sk_type == SOCK_RAW)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (sk->sk_protocol == IPPROTO_UDP) {
|
|
||||||
if (udp_sk(sk)->pending == AF_INET6) {
|
|
||||||
retv = -EBUSY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (sk->sk_protocol == IPPROTO_TCP) {
|
|
||||||
if (sk->sk_prot != &tcpv6_prot) {
|
|
||||||
retv = -EBUSY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sk->sk_state != TCP_ESTABLISHED) {
|
|
||||||
retv = -ENOTCONN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ipv6_only_sock(sk) ||
|
|
||||||
!ipv6_addr_v4mapped(&sk->sk_v6_daddr)) {
|
|
||||||
retv = -EADDRNOTAVAIL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
__ipv6_sock_mc_close(sk);
|
|
||||||
__ipv6_sock_ac_close(sk);
|
|
||||||
|
|
||||||
if (sk->sk_protocol == IPPROTO_TCP) {
|
|
||||||
struct inet_connection_sock *icsk = inet_csk(sk);
|
|
||||||
|
|
||||||
sock_prot_inuse_add(net, sk->sk_prot, -1);
|
|
||||||
sock_prot_inuse_add(net, &tcp_prot, 1);
|
|
||||||
|
|
||||||
/* Paired with READ_ONCE(sk->sk_prot) in inet6_stream_ops */
|
|
||||||
WRITE_ONCE(sk->sk_prot, &tcp_prot);
|
|
||||||
/* Paired with READ_ONCE() in tcp_(get|set)sockopt() */
|
|
||||||
WRITE_ONCE(icsk->icsk_af_ops, &ipv4_specific);
|
|
||||||
WRITE_ONCE(sk->sk_socket->ops, &inet_stream_ops);
|
|
||||||
WRITE_ONCE(sk->sk_family, PF_INET);
|
|
||||||
tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
|
|
||||||
} else {
|
|
||||||
sock_prot_inuse_add(net, sk->sk_prot, -1);
|
|
||||||
sock_prot_inuse_add(net, &udp_prot, 1);
|
|
||||||
|
|
||||||
/* Paired with READ_ONCE(sk->sk_prot) in inet6_dgram_ops */
|
|
||||||
WRITE_ONCE(sk->sk_prot, &udp_prot);
|
|
||||||
WRITE_ONCE(sk->sk_socket->ops, &inet_dgram_ops);
|
|
||||||
WRITE_ONCE(sk->sk_family, PF_INET);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disable all options not to allocate memory anymore,
|
|
||||||
* but there is still a race. See the lockless path
|
|
||||||
* in udpv6_sendmsg() and ipv6_local_rxpmtu().
|
|
||||||
*/
|
|
||||||
np->rxopt.all = 0;
|
|
||||||
|
|
||||||
inet6_cleanup_sock(sk);
|
|
||||||
|
|
||||||
module_put(THIS_MODULE);
|
|
||||||
retv = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
goto e_inval;
|
|
||||||
|
|
||||||
case IPV6_V6ONLY:
|
case IPV6_V6ONLY:
|
||||||
if (optlen < sizeof(int) ||
|
if (optlen < sizeof(int) ||
|
||||||
inet_sk(sk)->inet_num)
|
inet_sk(sk)->inet_num)
|
||||||
|
|
@ -1088,14 +1009,6 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
|
||||||
if (copy_from_sockptr(&len, optlen, sizeof(int)))
|
if (copy_from_sockptr(&len, optlen, sizeof(int)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
switch (optname) {
|
switch (optname) {
|
||||||
case IPV6_ADDRFORM:
|
|
||||||
if (sk->sk_protocol != IPPROTO_UDP &&
|
|
||||||
sk->sk_protocol != IPPROTO_TCP)
|
|
||||||
return -ENOPROTOOPT;
|
|
||||||
if (sk->sk_state != TCP_ESTABLISHED)
|
|
||||||
return -ENOTCONN;
|
|
||||||
val = sk->sk_family;
|
|
||||||
break;
|
|
||||||
case MCAST_MSFILTER:
|
case MCAST_MSFILTER:
|
||||||
if (in_compat_syscall())
|
if (in_compat_syscall())
|
||||||
return compat_ipv6_get_msfilter(sk, optval, optlen, len);
|
return compat_ipv6_get_msfilter(sk, optval, optlen, len);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user