Commit e804bd83 authored by Kuniyuki Iwashima's avatar Kuniyuki Iwashima Committed by Jakub Kicinski
Browse files

neighbour: Split pneigh_lookup().



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: default avatarKuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20250716221221.442239-6-kuniyu@google.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 0e5ac19c
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -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);

+29 −10
Original line number Diff line number Diff line
@@ -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>
@@ -748,22 +749,42 @@ 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 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;
+2 −2
Original line number Diff line number Diff line
@@ -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;
	}
+1 −1
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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;
		}