]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ipv6: mcast: Don't hold RTNL for IPV6_DROP_MEMBERSHIP and MCAST_LEAVE_GROUP.
authorKuniyuki Iwashima <kuniyu@google.com>
Wed, 2 Jul 2025 23:01:24 +0000 (16:01 -0700)
committerJakub Kicinski <kuba@kernel.org>
Wed, 9 Jul 2025 01:32:38 +0000 (18:32 -0700)
In __ipv6_sock_mc_drop(), per-socket mld data is protected by lock_sock(),
and only __dev_get_by_index() and __in6_dev_get() require RTNL.

Let's use dev_get_by_index() and in6_dev_get() and drop RTNL for
IPV6_ADD_MEMBERSHIP and MCAST_JOIN_GROUP.

Note that __ipv6_sock_mc_drop() is factorised to reuse in the next patch.

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20250702230210.3115355-8-kuni1840@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv6/ipv6_sockglue.c
net/ipv6/mcast.c

index cb0dc885cbe404d051cc8ad797d015bb6c94d74e..c8892d54821fb49a2f4308c996f73fe4a293bdae 100644 (file)
@@ -121,10 +121,8 @@ static bool setsockopt_needs_rtnl(int optname)
 {
        switch (optname) {
        case IPV6_ADDRFORM:
-       case IPV6_DROP_MEMBERSHIP:
        case IPV6_JOIN_ANYCAST:
        case IPV6_LEAVE_ANYCAST:
-       case MCAST_LEAVE_GROUP:
        case MCAST_JOIN_SOURCE_GROUP:
        case MCAST_LEAVE_SOURCE_GROUP:
        case MCAST_BLOCK_SOURCE:
index d55c1cb4189a4aee3e4c58a039c219dbac3720af..ed40f5b132ae2e220ee07dc0100b2be70889cc85 100644 (file)
@@ -253,14 +253,36 @@ int ipv6_sock_mc_join_ssm(struct sock *sk, int ifindex,
 /*
  *     socket leave on multicast group
  */
+static void __ipv6_sock_mc_drop(struct sock *sk, struct ipv6_mc_socklist *mc_lst)
+{
+       struct net *net = sock_net(sk);
+       struct net_device *dev;
+
+       dev = dev_get_by_index(net, mc_lst->ifindex);
+       if (dev) {
+               struct inet6_dev *idev = in6_dev_get(dev);
+
+               ip6_mc_leave_src(sk, mc_lst, idev);
+
+               if (idev) {
+                       __ipv6_dev_mc_dec(idev, &mc_lst->addr);
+                       in6_dev_put(idev);
+               }
+
+               dev_put(dev);
+       } else {
+               ip6_mc_leave_src(sk, mc_lst, NULL);
+       }
+
+       atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
+       kfree_rcu(mc_lst, rcu);
+}
+
 int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
-       struct ipv6_mc_socklist *mc_lst;
        struct ipv6_mc_socklist __rcu **lnk;
-       struct net *net = sock_net(sk);
-
-       ASSERT_RTNL();
+       struct ipv6_mc_socklist *mc_lst;
 
        if (!ipv6_addr_is_multicast(addr))
                return -EINVAL;
@@ -270,23 +292,8 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
              lnk = &mc_lst->next) {
                if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
                    ipv6_addr_equal(&mc_lst->addr, addr)) {
-                       struct net_device *dev;
-
                        *lnk = mc_lst->next;
-
-                       dev = __dev_get_by_index(net, mc_lst->ifindex);
-                       if (dev) {
-                               struct inet6_dev *idev = __in6_dev_get(dev);
-
-                               ip6_mc_leave_src(sk, mc_lst, idev);
-                               if (idev)
-                                       __ipv6_dev_mc_dec(idev, &mc_lst->addr);
-                       } else {
-                               ip6_mc_leave_src(sk, mc_lst, NULL);
-                       }
-
-                       atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
-                       kfree_rcu(mc_lst, rcu);
+                       __ipv6_sock_mc_drop(sk, mc_lst);
                        return 0;
                }
        }