]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
neighbour: Split pneigh_lookup().
authorKuniyuki Iwashima <kuniyu@google.com>
Wed, 16 Jul 2025 22:08:10 +0000 (22:08 +0000)
committerJakub Kicinski <kuba@kernel.org>
Thu, 17 Jul 2025 23:25:20 +0000 (16:25 -0700)
pneigh_lookup() has ASSERT_RTNL() in the middle of the function, which
is confusing.

When called with the last argument, creat, 0, pneigh_lookup() literally
looks up a proxy neighbour entry.  This is the case of the reader path
as the fast path and RTM_GETNEIGH.

pneigh_lookup(), however, creates a pneigh_entry when called with creat 1
from RTM_NEWNEIGH and SIOCSARP, which require RTNL.

Let's split pneigh_lookup() into two functions.

We will convert all the reader paths to RCU, and read_lock_bh(&tbl->lock)
in the new pneigh_lookup() will be dropped.

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20250716221221.442239-6-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/net/neighbour.h
net/core/neighbour.c
net/ipv4/arp.c
net/ipv6/ip6_output.c
net/ipv6/ndisc.c

index 7e865b14749d6842c322e0a0f6bc75d55f79a3cc..7f3d57da5689a892f540905dd4ac1559509178c9 100644 (file)
@@ -376,10 +376,11 @@ unsigned long neigh_rand_reach_time(unsigned long base);
 void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
                    struct sk_buff *skb);
 struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl, struct net *net,
-                                  const void *key, struct net_device *dev,
-                                  int creat);
+                                  const void *key, struct net_device *dev);
 struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl, struct net *net,
                                     const void *key, struct net_device *dev);
+struct pneigh_entry *pneigh_create(struct neigh_table *tbl, struct net *net,
+                                  const void *key, struct net_device *dev);
 int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *key,
                  struct net_device *dev);
 
index ffb8d80328ed76cbc855d28ad6d18afba27b03d5..d0e303360b2c4166f7f90b703748a603daab4be8 100644 (file)
@@ -28,6 +28,7 @@
 #include <net/neighbour.h>
 #include <net/arp.h>
 #include <net/dst.h>
+#include <net/ip.h>
 #include <net/sock.h>
 #include <net/netevent.h>
 #include <net/netlink.h>
@@ -746,24 +747,44 @@ struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl,
 }
 EXPORT_SYMBOL_GPL(__pneigh_lookup);
 
-struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl,
-                                   struct net *net, const void *pkey,
-                                   struct net_device *dev, int creat)
+struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,
+                                  struct net *net, const void *pkey,
+                                  struct net_device *dev)
+{
+       struct pneigh_entry *n;
+       unsigned int key_len;
+       u32 hash_val;
+
+       key_len = tbl->key_len;
+       hash_val = pneigh_hash(pkey, key_len);
+
+       read_lock_bh(&tbl->lock);
+       n = __pneigh_lookup_1(tbl->phash_buckets[hash_val],
+                             net, pkey, key_len, dev);
+       read_unlock_bh(&tbl->lock);
+
+       return n;
+}
+EXPORT_IPV6_MOD(pneigh_lookup);
+
+struct pneigh_entry *pneigh_create(struct neigh_table *tbl,
+                                  struct net *net, const void *pkey,
+                                  struct net_device *dev)
 {
        struct pneigh_entry *n;
        unsigned int key_len = tbl->key_len;
        u32 hash_val = pneigh_hash(pkey, key_len);
 
+       ASSERT_RTNL();
+
        read_lock_bh(&tbl->lock);
        n = __pneigh_lookup_1(tbl->phash_buckets[hash_val],
                              net, pkey, key_len, dev);
        read_unlock_bh(&tbl->lock);
 
-       if (n || !creat)
+       if (n)
                goto out;
 
-       ASSERT_RTNL();
-
        n = kzalloc(sizeof(*n) + key_len, GFP_KERNEL);
        if (!n)
                goto out;
@@ -787,8 +808,6 @@ struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl,
 out:
        return n;
 }
-EXPORT_SYMBOL(pneigh_lookup);
-
 
 int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,
                  struct net_device *dev)
@@ -2007,7 +2026,7 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
                }
 
                err = -ENOBUFS;
-               pn = pneigh_lookup(tbl, net, dst, dev, 1);
+               pn = pneigh_create(tbl, net, dst, dev);
                if (pn) {
                        pn->flags = ndm_flags;
                        pn->permanent = !!(ndm->ndm_state & NUD_PERMANENT);
@@ -3036,7 +3055,7 @@ static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
        if (ndm->ndm_flags & NTF_PROXY) {
                struct pneigh_entry *pn;
 
-               pn = pneigh_lookup(tbl, net, dst, dev, 0);
+               pn = pneigh_lookup(tbl, net, dst, dev);
                if (!pn) {
                        NL_SET_ERR_MSG(extack, "Proxy neighbour entry not found");
                        err = -ENOENT;
index c0440d61cf2ffda7df544b641a239ef9e7e90d79..d93b5735b0ba406d712ca526cfe1ff85ebf961cc 100644 (file)
@@ -864,7 +864,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
                            (arp_fwd_proxy(in_dev, dev, rt) ||
                             arp_fwd_pvlan(in_dev, dev, rt, sip, tip) ||
                             (rt->dst.dev != dev &&
-                             pneigh_lookup(&arp_tbl, net, &tip, dev, 0)))) {
+                             pneigh_lookup(&arp_tbl, net, &tip, dev)))) {
                                n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
                                if (n)
                                        neigh_release(n);
@@ -1089,7 +1089,7 @@ static int arp_req_set_public(struct net *net, struct arpreq *r,
        if (mask) {
                __be32 ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
 
-               if (!pneigh_lookup(&arp_tbl, net, &ip, dev, 1))
+               if (!pneigh_create(&arp_tbl, net, &ip, dev))
                        return -ENOBUFS;
                return 0;
        }
index fcc20c7250eb03077882e97bc0273b42f5b57aaf..0412f85446958e25b8c9838f1e956114a0b2ac6b 100644 (file)
@@ -563,7 +563,7 @@ int ip6_forward(struct sk_buff *skb)
 
        /* XXX: idev->cnf.proxy_ndp? */
        if (READ_ONCE(net->ipv6.devconf_all->proxy_ndp) &&
-           pneigh_lookup(&nd_tbl, net, &hdr->daddr, skb->dev, 0)) {
+           pneigh_lookup(&nd_tbl, net, &hdr->daddr, skb->dev)) {
                int proxied = ip6_forward_proxy_check(skb);
                if (proxied > 0) {
                        /* It's tempting to decrease the hop limit
index d4c5876e1771880f9ea59ce44d34bc7608ec1ac2..a3ac26c1df6d81f2f58762d5911cab50a70e7caf 100644 (file)
@@ -1100,7 +1100,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
                if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
                    READ_ONCE(net->ipv6.devconf_all->forwarding) &&
                    READ_ONCE(net->ipv6.devconf_all->proxy_ndp) &&
-                   pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
+                   pneigh_lookup(&nd_tbl, net, &msg->target, dev)) {
                        /* XXX: idev->cnf.proxy_ndp */
                        goto out;
                }