]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ipv6: anycast: Don't hold RTNL for IPV6_JOIN_ANYCAST.
authorKuniyuki Iwashima <kuniyu@google.com>
Wed, 2 Jul 2025 23:01:31 +0000 (16:01 -0700)
committerJakub Kicinski <kuba@kernel.org>
Wed, 9 Jul 2025 01:32:39 +0000 (18:32 -0700)
inet6_sk(sk)->ipv6_ac_list is protected by lock_sock().

In ipv6_sock_ac_join(), only __dev_get_by_index(), __dev_get_by_flags(),
and __in6_dev_get() require RTNL.

__dev_get_by_flags() is only used by ipv6_sock_ac_join() and can be
converted to RCU version.

Let's replace RCU version helper and drop RTNL from IPV6_JOIN_ANYCAST.

setsockopt_needs_rtnl() will be removed 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-15-kuni1840@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/linux/netdevice.h
net/core/dev.c
net/ipv6/anycast.c
net/ipv6/ipv6_sockglue.c

index 5847c20994d3661f12163959d5d0c9c130aeaef4..a80d21a146123dd46fdb9701e67d7dc712b3491a 100644 (file)
@@ -3332,8 +3332,8 @@ int dev_get_iflink(const struct net_device *dev);
 int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
 int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
                          struct net_device_path_stack *stack);
-struct net_device *__dev_get_by_flags(struct net *net, unsigned short flags,
-                                     unsigned short mask);
+struct net_device *dev_get_by_flags_rcu(struct net *net, unsigned short flags,
+                                       unsigned short mask);
 struct net_device *dev_get_by_name(struct net *net, const char *name);
 struct net_device *dev_get_by_name_rcu(struct net *net, const char *name);
 struct net_device *__dev_get_by_name(struct net *net, const char *name);
index fe677ccec5b03567b9c4ef89ea881651af468fbe..e365b099484ec423435b4019f00dd18cc92f583f 100644 (file)
@@ -1267,33 +1267,31 @@ struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type)
 EXPORT_SYMBOL(dev_getfirstbyhwtype);
 
 /**
- *     __dev_get_by_flags - find any device with given flags
- *     @net: the applicable net namespace
- *     @if_flags: IFF_* values
- *     @mask: bitmask of bits in if_flags to check
+ * dev_get_by_flags_rcu - find any device with given flags
+ * @net: the applicable net namespace
+ * @if_flags: IFF_* values
+ * @mask: bitmask of bits in if_flags to check
  *
- *     Search for any interface with the given flags. Returns NULL if a device
- *     is not found or a pointer to the device. Must be called inside
- *     rtnl_lock(), and result refcount is unchanged.
+ * Search for any interface with the given flags.
+ *
+ * Context: rcu_read_lock() must be held.
+ * Returns: NULL if a device is not found or a pointer to the device.
  */
-
-struct net_device *__dev_get_by_flags(struct net *net, unsigned short if_flags,
-                                     unsigned short mask)
+struct net_device *dev_get_by_flags_rcu(struct net *net, unsigned short if_flags,
+                                       unsigned short mask)
 {
-       struct net_device *dev, *ret;
-
-       ASSERT_RTNL();
+       struct net_device *dev;
 
-       ret = NULL;
-       for_each_netdev(net, dev) {
-               if (((dev->flags ^ if_flags) & mask) == 0) {
-                       ret = dev;
-                       break;
+       for_each_netdev_rcu(net, dev) {
+               if (((READ_ONCE(dev->flags) ^ if_flags) & mask) == 0) {
+                       dev_hold(dev);
+                       return dev;
                }
        }
-       return ret;
+
+       return NULL;
 }
-EXPORT_SYMBOL(__dev_get_by_flags);
+EXPORT_IPV6_MOD(dev_get_by_flags_rcu);
 
 /**
  *     dev_valid_name - check if name is okay for network device
index fd3d104c6c05764dc921ed342c8bdace12e2e1d0..53cf68e0242bfd0136d9edb368526477bde5c38c 100644 (file)
@@ -73,15 +73,13 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
        struct inet6_dev *idev;
        int err = 0, ishost;
 
-       ASSERT_RTNL();
-
        if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
                return -EPERM;
        if (ipv6_addr_is_multicast(addr))
                return -EINVAL;
 
        if (ifindex)
-               dev = __dev_get_by_index(net, ifindex);
+               dev = dev_get_by_index(net, ifindex);
 
        if (ipv6_chk_addr_and_flags(net, addr, dev, true, 0, IFA_F_TENTATIVE)) {
                err = -EINVAL;
@@ -102,18 +100,22 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
        if (ifindex == 0) {
                struct rt6_info *rt;
 
+               rcu_read_lock();
                rt = rt6_lookup(net, addr, NULL, 0, NULL, 0);
                if (rt) {
-                       dev = rt->dst.dev;
+                       dev = dst_dev(&rt->dst);
+                       dev_hold(dev);
                        ip6_rt_put(rt);
                } else if (ishost) {
+                       rcu_read_unlock();
                        err = -EADDRNOTAVAIL;
                        goto error;
                } else {
                        /* router, no matching interface: just pick one */
-                       dev = __dev_get_by_flags(net, IFF_UP,
-                                                IFF_UP | IFF_LOOPBACK);
+                       dev = dev_get_by_flags_rcu(net, IFF_UP,
+                                                  IFF_UP | IFF_LOOPBACK);
                }
+               rcu_read_unlock();
        }
 
        if (!dev) {
@@ -121,7 +123,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
                goto error;
        }
 
-       idev = __in6_dev_get(dev);
+       idev = in6_dev_get(dev);
        if (!idev) {
                if (ifindex)
                        err = -ENODEV;
@@ -144,7 +146,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
                if (ishost)
                        err = -EADDRNOTAVAIL;
                if (err)
-                       goto error;
+                       goto error_idev;
        }
 
        err = __ipv6_dev_ac_inc(idev, addr);
@@ -154,7 +156,11 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
                pac = NULL;
        }
 
+error_idev:
+       in6_dev_put(idev);
 error:
+       dev_put(dev);
+
        if (pac)
                sock_kfree_s(sk, pac, sizeof(*pac));
        return err;
index 3d891aa6e7f522b7e1d11a9846a38af7991ad336..702dc33e50ad79bfb53c871015be94f2e7fd9527 100644 (file)
@@ -119,10 +119,6 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
 
 static bool setsockopt_needs_rtnl(int optname)
 {
-       switch (optname) {
-       case IPV6_JOIN_ANYCAST:
-               return true;
-       }
        return false;
 }