Skip to content

Commit 22701a3

Browse files
CKI Backport BotHangbin Liu
authored andcommitted
ipv6: mcast: Don't hold RTNL for MCAST_ socket options.
JIRA: https://issues.redhat.com/browse/RHEL-115325 commit e6e14d5 Author: Kuniyuki Iwashima <kuniyu@google.com> Date: Wed Jul 2 16:01:26 2025 -0700 ipv6: mcast: Don't hold RTNL for MCAST_ socket options. In ip6_mc_source() and ip6_mc_msfilter(), per-socket mld data is protected by lock_sock() and inet6_dev->mc_lock is also held for some per-interface functions. ip6_mc_find_dev_rtnl() only depends on RTNL. If we want to remove it, we need to check inet6_dev->dead under mc_lock to close the race with addrconf_ifdown(), as mentioned earlier. Let's do that and drop RTNL for the rest of MCAST_ socket options. Note that ip6_mc_msfilter() has unnecessary lock dances and they are integrated into one to avoid the last-minute error and simplify the error handling. Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Link: https://patch.msgid.link/20250702230210.3115355-10-kuni1840@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com>
1 parent bc53b02 commit 22701a3

File tree

2 files changed

+45
-34
lines changed

2 files changed

+45
-34
lines changed

net/ipv6/ipv6_sockglue.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,6 @@ static bool setsockopt_needs_rtnl(int optname)
123123
case IPV6_ADDRFORM:
124124
case IPV6_JOIN_ANYCAST:
125125
case IPV6_LEAVE_ANYCAST:
126-
case MCAST_JOIN_SOURCE_GROUP:
127-
case MCAST_LEAVE_SOURCE_GROUP:
128-
case MCAST_BLOCK_SOURCE:
129-
case MCAST_UNBLOCK_SOURCE:
130-
case MCAST_MSFILTER:
131126
return true;
132127
}
133128
return false;

net/ipv6/mcast.c

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -302,31 +302,36 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
302302
}
303303
EXPORT_SYMBOL(ipv6_sock_mc_drop);
304304

305-
static struct inet6_dev *ip6_mc_find_dev_rtnl(struct net *net,
306-
const struct in6_addr *group,
307-
int ifindex)
305+
static struct inet6_dev *ip6_mc_find_dev(struct net *net,
306+
const struct in6_addr *group,
307+
int ifindex)
308308
{
309309
struct net_device *dev = NULL;
310-
struct inet6_dev *idev = NULL;
310+
struct inet6_dev *idev;
311311

312312
if (ifindex == 0) {
313-
struct rt6_info *rt = rt6_lookup(net, group, NULL, 0, NULL, 0);
313+
struct rt6_info *rt;
314314

315+
rcu_read_lock();
316+
rt = rt6_lookup(net, group, NULL, 0, NULL, 0);
315317
if (rt) {
316-
dev = rt->dst.dev;
318+
dev = dst_dev(&rt->dst);
319+
dev_hold(dev);
317320
ip6_rt_put(rt);
318321
}
322+
rcu_read_unlock();
319323
} else {
320-
dev = __dev_get_by_index(net, ifindex);
324+
dev = dev_get_by_index(net, ifindex);
321325
}
322-
323326
if (!dev)
324327
return NULL;
325-
idev = __in6_dev_get(dev);
328+
329+
idev = in6_dev_get(dev);
330+
dev_put(dev);
331+
326332
if (!idev)
327333
return NULL;
328-
if (idev->dead)
329-
return NULL;
334+
330335
return idev;
331336
}
332337

@@ -354,16 +359,16 @@ void ipv6_sock_mc_close(struct sock *sk)
354359
}
355360

356361
int ip6_mc_source(int add, int omode, struct sock *sk,
357-
struct group_source_req *pgsr)
362+
struct group_source_req *pgsr)
358363
{
364+
struct ipv6_pinfo *inet6 = inet6_sk(sk);
359365
struct in6_addr *source, *group;
366+
struct net *net = sock_net(sk);
360367
struct ipv6_mc_socklist *pmc;
361-
struct inet6_dev *idev;
362-
struct ipv6_pinfo *inet6 = inet6_sk(sk);
363368
struct ip6_sf_socklist *psl;
364-
struct net *net = sock_net(sk);
365-
int i, j, rv;
369+
struct inet6_dev *idev;
366370
int leavegroup = 0;
371+
int i, j, rv;
367372
int err;
368373

369374
source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr;
@@ -372,13 +377,19 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
372377
if (!ipv6_addr_is_multicast(group))
373378
return -EINVAL;
374379

375-
idev = ip6_mc_find_dev_rtnl(net, group, pgsr->gsr_interface);
380+
idev = ip6_mc_find_dev(net, group, pgsr->gsr_interface);
376381
if (!idev)
377382
return -ENODEV;
378383

384+
mutex_lock(&idev->mc_lock);
385+
386+
if (idev->dead) {
387+
err = -ENODEV;
388+
goto done;
389+
}
390+
379391
err = -EADDRNOTAVAIL;
380392

381-
mutex_lock(&idev->mc_lock);
382393
for_each_pmc_socklock(inet6, sk, pmc) {
383394
if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface)
384395
continue;
@@ -475,6 +486,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
475486
ip6_mc_add_src(idev, group, omode, 1, source, 1);
476487
done:
477488
mutex_unlock(&idev->mc_lock);
489+
in6_dev_put(idev);
478490
if (leavegroup)
479491
err = ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group);
480492
return err;
@@ -483,12 +495,12 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
483495
int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf,
484496
struct sockaddr_storage *list)
485497
{
486-
const struct in6_addr *group;
487-
struct ipv6_mc_socklist *pmc;
488-
struct inet6_dev *idev;
489498
struct ipv6_pinfo *inet6 = inet6_sk(sk);
490499
struct ip6_sf_socklist *newpsl, *psl;
491500
struct net *net = sock_net(sk);
501+
const struct in6_addr *group;
502+
struct ipv6_mc_socklist *pmc;
503+
struct inet6_dev *idev;
492504
int leavegroup = 0;
493505
int i, err;
494506

@@ -500,10 +512,17 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf,
500512
gsf->gf_fmode != MCAST_EXCLUDE)
501513
return -EINVAL;
502514

503-
idev = ip6_mc_find_dev_rtnl(net, group, gsf->gf_interface);
515+
idev = ip6_mc_find_dev(net, group, gsf->gf_interface);
504516
if (!idev)
505517
return -ENODEV;
506518

519+
mutex_lock(&idev->mc_lock);
520+
521+
if (idev->dead) {
522+
err = -ENODEV;
523+
goto done;
524+
}
525+
507526
err = 0;
508527

509528
if (gsf->gf_fmode == MCAST_INCLUDE && gsf->gf_numsrc == 0) {
@@ -536,24 +555,19 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf,
536555
psin6 = (struct sockaddr_in6 *)list;
537556
newpsl->sl_addr[i] = psin6->sin6_addr;
538557
}
539-
mutex_lock(&idev->mc_lock);
558+
540559
err = ip6_mc_add_src(idev, group, gsf->gf_fmode,
541560
newpsl->sl_count, newpsl->sl_addr, 0);
542561
if (err) {
543-
mutex_unlock(&idev->mc_lock);
544562
sock_kfree_s(sk, newpsl, struct_size(newpsl, sl_addr,
545563
newpsl->sl_max));
546564
goto done;
547565
}
548-
mutex_unlock(&idev->mc_lock);
549566
} else {
550567
newpsl = NULL;
551-
mutex_lock(&idev->mc_lock);
552568
ip6_mc_add_src(idev, group, gsf->gf_fmode, 0, NULL, 0);
553-
mutex_unlock(&idev->mc_lock);
554569
}
555570

556-
mutex_lock(&idev->mc_lock);
557571
psl = sock_dereference(pmc->sflist, sk);
558572
if (psl) {
559573
ip6_mc_del_src(idev, group, pmc->sfmode,
@@ -563,12 +577,14 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf,
563577
} else {
564578
ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0);
565579
}
580+
566581
rcu_assign_pointer(pmc->sflist, newpsl);
567-
mutex_unlock(&idev->mc_lock);
568582
kfree_rcu(psl, rcu);
569583
pmc->sfmode = gsf->gf_fmode;
570584
err = 0;
571585
done:
586+
mutex_unlock(&idev->mc_lock);
587+
in6_dev_put(idev);
572588
if (leavegroup)
573589
err = ipv6_sock_mc_drop(sk, gsf->gf_interface, group);
574590
return err;

0 commit comments

Comments
 (0)