sctp: Use sk_clone() in sctp_accept().

sctp_accept() calls sctp_v[46]_create_accept_sk() to allocate a new
socket and calls sctp_sock_migrate() to copy fields from the parent
socket to the new socket.

sctp_v4_create_accept_sk() allocates sk by sk_alloc(), initialises
it by sock_init_data(), and copy a bunch of fields from the parent
socekt by sctp_copy_sock().

sctp_sock_migrate() calls sctp_copy_descendant() to copy most fields
in sctp_sock from the parent socket by memcpy().

These can be simply replaced by sk_clone().

Let's consolidate sctp_v[46]_create_accept_sk() to sctp_clone_sock()
with sk_clone().

We will reuse sctp_clone_sock() for sctp_do_peeloff() and then remove
sctp_copy_descendant().

Note that sock_reset_flag(newsk, SOCK_ZAPPED) is not copied to
sctp_clone_sock() as sctp does not use SOCK_ZAPPED at all.

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Acked-by: Xin Long <lucien.xin@gmail.com>
Link: https://patch.msgid.link/20251023231751.4168390-6-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Kuniyuki Iwashima
2025-10-23 23:16:54 +00:00
committed by Jakub Kicinski
parent 151b98d10e
commit 16942cf4d3
2 changed files with 77 additions and 40 deletions

View File

@@ -755,9 +755,7 @@ EXPORT_SYMBOL(inet_stream_connect);
void __inet_accept(struct socket *sock, struct socket *newsock, struct sock *newsk)
{
/* TODO: use sk_clone_lock() in SCTP and remove protocol checks */
if (mem_cgroup_sockets_enabled &&
(!IS_ENABLED(CONFIG_IP_SCTP) || sk_is_tcp(newsk))) {
if (mem_cgroup_sockets_enabled) {
gfp_t gfp = GFP_KERNEL | __GFP_NOFAIL;
mem_cgroup_sk_alloc(newsk);

View File

@@ -4842,6 +4842,74 @@ static int sctp_disconnect(struct sock *sk, int flags)
return 0;
}
static struct sock *sctp_clone_sock(struct sock *sk,
struct sctp_association *asoc,
enum sctp_socket_type type)
{
struct sock *newsk = sk_clone(sk, GFP_KERNEL, false);
struct inet_sock *newinet;
struct sctp_sock *newsp;
int err = -ENOMEM;
if (!newsk)
return ERR_PTR(err);
/* sk_clone() sets refcnt to 2 */
sock_put(newsk);
newinet = inet_sk(newsk);
newsp = sctp_sk(newsk);
newsp->pf->to_sk_daddr(&asoc->peer.primary_addr, newsk);
newinet->inet_dport = htons(asoc->peer.port);
newsp->pf->copy_ip_options(sk, newsk);
atomic_set(&newinet->inet_id, get_random_u16());
inet_set_bit(MC_LOOP, newsk);
newinet->mc_ttl = 1;
newinet->mc_index = 0;
newinet->mc_list = NULL;
#if IS_ENABLED(CONFIG_IPV6)
if (sk->sk_family == AF_INET6) {
struct ipv6_pinfo *newnp = inet6_sk(newsk);
newinet->pinet6 = &((struct sctp6_sock *)newsk)->inet6;
newinet->ipv6_fl_list = NULL;
memcpy(newnp, inet6_sk(sk), sizeof(struct ipv6_pinfo));
newnp->ipv6_mc_list = NULL;
newnp->ipv6_ac_list = NULL;
}
#endif
skb_queue_head_init(&newsp->pd_lobby);
newsp->ep = sctp_endpoint_new(newsk, GFP_KERNEL);
if (!newsp->ep)
goto out_release;
SCTP_DBG_OBJCNT_INC(sock);
sk_sockets_allocated_inc(newsk);
sock_prot_inuse_add(sock_net(sk), newsk->sk_prot, 1);
err = sctp_sock_migrate(sk, newsk, asoc, type);
if (err)
goto out_release;
/* Set newsk security attributes from original sk and connection
* security attribute from asoc.
*/
security_sctp_sk_clone(asoc, sk, newsk);
return newsk;
out_release:
sk_common_release(newsk);
return ERR_PTR(err);
}
/* 4.1.4 accept() - TCP Style Syntax
*
* Applications use accept() call to remove an established SCTP
@@ -4851,18 +4919,13 @@ static int sctp_disconnect(struct sock *sk, int flags)
*/
static struct sock *sctp_accept(struct sock *sk, struct proto_accept_arg *arg)
{
struct sctp_sock *sp, *newsp;
struct sctp_endpoint *ep;
struct sock *newsk = NULL;
struct sctp_association *asoc;
long timeo;
struct sock *newsk = NULL;
int error = 0;
long timeo;
lock_sock(sk);
sp = sctp_sk(sk);
ep = sp->ep;
if (!sctp_style(sk, TCP)) {
error = -EOPNOTSUPP;
goto out;
@@ -4883,43 +4946,19 @@ static struct sock *sctp_accept(struct sock *sk, struct proto_accept_arg *arg)
/* We treat the list of associations on the endpoint as the accept
* queue and pick the first association on the list.
*/
asoc = list_entry(ep->asocs.next, struct sctp_association, asocs);
asoc = list_entry(sctp_sk(sk)->ep->asocs.next,
struct sctp_association, asocs);
newsk = sp->pf->create_accept_sk(sk, asoc, arg->kern);
if (!newsk) {
error = -ENOMEM;
goto out;
newsk = sctp_clone_sock(sk, asoc, SCTP_SOCKET_TCP);
if (IS_ERR(newsk)) {
error = PTR_ERR(newsk);
newsk = NULL;
}
newsp = sctp_sk(newsk);
newsp->ep = sctp_endpoint_new(newsk, GFP_KERNEL);
if (!newsp->ep) {
error = -ENOMEM;
goto out_release;
}
skb_queue_head_init(&newsp->pd_lobby);
sk_sockets_allocated_inc(newsk);
sock_prot_inuse_add(sock_net(sk), newsk->sk_prot, 1);
SCTP_DBG_OBJCNT_INC(sock);
/* Populate the fields of the newsk from the oldsk and migrate the
* asoc to the newsk.
*/
error = sctp_sock_migrate(sk, newsk, asoc, SCTP_SOCKET_TCP);
if (error)
goto out_release;
out:
release_sock(sk);
arg->err = error;
return newsk;
out_release:
sk_common_release(newsk);
newsk = NULL;
goto out;
}
/* The SCTP ioctl handler. */