Netdev List
 help / color / mirror / Atom feed
* [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends.
@ 2026-10-03 21:23 Kuniyuki Iwashima
  2026-10-03 21:23 ` [PATCH v2 net-next 1/9] ipv4: Rename ipv4_neigh_lookup() to ipv4_dst_neigh_lookup() Kuniyuki Iwashima
                   ` (11 more replies)
  0 siblings, 12 replies; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-03 21:23 UTC (permalink / raw)
  To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Ahern, Ido Schimmel
  Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev

syzbot reported that ip6_finish_output2() could pass tbl and dev
from different netns to neigh_create().

When namespacifying neigh_table, I chose to resolve neigh_table
in callers to minimise changes, but it turned out to be error-prone.

This series adds IPv4/IPv6-specific helpers for neigh_lookup()
and neigh_create() that always fetch the netns from dev to fix
the problem.

Along the way, the confusing and now mostly redundant helpers
__neigh_lookup() and __neigh_lookup_errno() are converted and
removed.


Changes:
  v2:
    * Add Patch 9
    * Patch 6 & 7: Convert __teql_resolve()

  v1: https://lore.kernel.org/netdev/20260930200326.718459-1-kuniyu@google.com/


Kuniyuki Iwashima (9):
  ipv4: Rename ipv4_neigh_lookup() to ipv4_dst_neigh_lookup().
  ipv6: Rename ip6_neigh_lookup() to __ip6_dst_neigh_lookup().
  ipv4: Add wrappers for neigh_lookup() and neigh_create().
  ipv6: Add wrappers for neigh_lookup() and (__)?neigh_create().
  mlxsw: spectrum: Resolve neigh_table right before neigh_lookup().
  ipv6: Use ipv6_neigh_lookup() and friends.
  ipv4: Use ipv4_neigh_lookup() and friends.
  neighbour: Remove __neigh_lookup() and __neigh_lookup_errno().
  neighbour: Check n->parms->tbl under tbl->lock in ___neigh_create().

 .../marvell/prestera/prestera_router.c        | 14 ++--
 .../mellanox/mlx5/core/en/tc_tun_encap.c      | 14 ++--
 .../mellanox/mlx5/core/en_accel/ipsec.c       |  6 +-
 .../ethernet/mellanox/mlxsw/spectrum_router.c | 77 +++++++++++--------
 .../ethernet/mellanox/mlxsw/spectrum_span.c   | 42 ++++++----
 .../netronome/nfp/flower/tunnel_conf.c        |  8 +-
 drivers/net/ethernet/rocker/rocker_ofdpa.c    |  2 +-
 drivers/net/ethernet/sfc/tc_counters.c        | 11 +--
 drivers/net/vrf.c                             |  4 +-
 drivers/net/vxlan/vxlan_core.c                | 14 +---
 include/net/arp.h                             | 16 ++++
 include/net/ip6_route.h                       |  6 +-
 include/net/ndisc.h                           | 31 ++++++--
 include/net/neighbour.h                       | 24 ------
 net/bridge/br_arp_nd_proxy.c                  |  4 +-
 net/core/neighbour.c                          | 11 ++-
 net/ieee802154/6lowpan/tx.c                   |  3 +-
 net/ipv4/arp.c                                | 26 ++++---
 net/ipv4/fib_semantics.c                      |  5 +-
 net/ipv4/route.c                              | 18 ++---
 net/ipv6/ip6_output.c                         |  2 +-
 net/ipv6/ndisc.c                              | 48 +++++++-----
 net/ipv6/route.c                              | 23 +++---
 net/sched/sch_teql.c                          | 15 +++-
 24 files changed, 234 insertions(+), 190 deletions(-)

-- 
2.56.0.rc1.315.gc6ed9934b7-goog


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v2 net-next 1/9] ipv4: Rename ipv4_neigh_lookup() to ipv4_dst_neigh_lookup().
  2026-10-03 21:23 [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Kuniyuki Iwashima
@ 2026-10-03 21:23 ` Kuniyuki Iwashima
  2026-10-03 21:23 ` [PATCH v2 net-next 2/9] ipv6: Rename ip6_neigh_lookup() to __ip6_dst_neigh_lookup() Kuniyuki Iwashima
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-03 21:23 UTC (permalink / raw)
  To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Ahern, Ido Schimmel
  Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev

Later, we will add ipv4_neigh_lookup() as a wrapper for
neigh_lookup(arp_table(net), ...).

The existing ipv4_neigh_lookup() is more like ip6_dst_neigh_lookup().

Let's rename ipv4_neigh_lookup() to ipv4_dst_neigh_lookup().

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 net/ipv4/route.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index d7da2f1acbb5..50c842617e45 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -146,9 +146,9 @@ static u32 *ipv4_cow_metrics(struct dst_entry *dst, unsigned long old)
 	return NULL;
 }
 
-static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
-					   struct sk_buff *skb,
-					   const void *daddr);
+static struct neighbour *ipv4_dst_neigh_lookup(const struct dst_entry *dst,
+					       struct sk_buff *skb,
+					       const void *daddr);
 static void ipv4_confirm_neigh(const struct dst_entry *dst, const void *daddr);
 
 static struct dst_ops ipv4_dst_ops = {
@@ -163,7 +163,7 @@ static struct dst_ops ipv4_dst_ops = {
 	.update_pmtu =		ip_rt_update_pmtu,
 	.redirect =		ip_do_redirect,
 	.local_out =		__ip_local_out,
-	.neigh_lookup =		ipv4_neigh_lookup,
+	.neigh_lookup =		ipv4_dst_neigh_lookup,
 	.confirm_neigh =	ipv4_confirm_neigh,
 };
 
@@ -409,9 +409,9 @@ void rt_cache_flush(struct net *net)
 	rt_genid_bump_ipv4(net);
 }
 
-static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
-					   struct sk_buff *skb,
-					   const void *daddr)
+static struct neighbour *ipv4_dst_neigh_lookup(const struct dst_entry *dst,
+					       struct sk_buff *skb,
+					       const void *daddr)
 {
 	const struct rtable *rt = container_of(dst, struct rtable, dst);
 	struct net_device *dev;
@@ -2910,7 +2910,7 @@ struct rtable *ip_route_output_key_hash_rcu(struct net *net, struct flowi4 *fl4,
 static struct dst_ops ipv4_dst_blackhole_ops = {
 	.family			= AF_INET,
 	.default_advmss		= ipv4_default_advmss,
-	.neigh_lookup		= ipv4_neigh_lookup,
+	.neigh_lookup		= ipv4_dst_neigh_lookup,
 	.check			= dst_blackhole_check,
 	.cow_metrics		= dst_blackhole_cow_metrics,
 	.update_pmtu		= dst_blackhole_update_pmtu,
-- 
2.56.0.rc1.315.gc6ed9934b7-goog


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 net-next 2/9] ipv6: Rename ip6_neigh_lookup() to __ip6_dst_neigh_lookup().
  2026-10-03 21:23 [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Kuniyuki Iwashima
  2026-10-03 21:23 ` [PATCH v2 net-next 1/9] ipv4: Rename ipv4_neigh_lookup() to ipv4_dst_neigh_lookup() Kuniyuki Iwashima
@ 2026-10-03 21:23 ` Kuniyuki Iwashima
  2026-10-03 21:23 ` [PATCH v2 net-next 3/9] ipv4: Add wrappers for neigh_lookup() and neigh_create() Kuniyuki Iwashima
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-03 21:23 UTC (permalink / raw)
  To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Ahern, Ido Schimmel
  Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev

Later, we will add ipv6_neigh_lookup() as a wrapper for
neigh_lookup(nd_table(net), ...).

The existing ip6_neigh_lookup() is called from ip6_dst_neigh_lookup()
with a gateway address.

Let's rename ip6_neigh_lookup() to __ip6_dst_neigh_lookup().

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 include/net/ip6_route.h |  6 +++---
 net/ipv6/ndisc.c        | 12 ++++++------
 net/ipv6/route.c        | 12 ++++++------
 3 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 0f9b7a260d25..5a21ed4b6e1c 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -430,7 +430,7 @@ u32 ip6_mtu_from_fib6(const struct fib6_result *res,
 		      const struct in6_addr *daddr,
 		      const struct in6_addr *saddr);
 
-struct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,
-				   struct net_device *dev, struct sk_buff *skb,
-				   const void *daddr);
+struct neighbour *__ip6_dst_neigh_lookup(const struct in6_addr *gw,
+					 struct net_device *dev, struct sk_buff *skb,
+					 const void *daddr);
 #endif
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 12d85d7f8234..e1cbffaa0ad0 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1359,9 +1359,9 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
 	/* routes added from RAs do not use nexthop objects */
 	rt = rt6_get_dflt_router(net, &ipv6_hdr(skb)->saddr, skb->dev);
 	if (rt) {
-		neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6,
-					 rt->fib6_nh->fib_nh_dev, NULL,
-					  &ipv6_hdr(skb)->saddr);
+		neigh = __ip6_dst_neigh_lookup(&rt->fib6_nh->fib_nh_gw6,
+					       rt->fib6_nh->fib_nh_dev, NULL,
+					       &ipv6_hdr(skb)->saddr);
 		if (!neigh) {
 			net_err_ratelimited("RA: %s got default router without neighbour\n",
 					    __func__);
@@ -1395,9 +1395,9 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
 			return reason;
 		}
 
-		neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6,
-					 rt->fib6_nh->fib_nh_dev, NULL,
-					  &ipv6_hdr(skb)->saddr);
+		neigh = __ip6_dst_neigh_lookup(&rt->fib6_nh->fib_nh_gw6,
+					       rt->fib6_nh->fib_nh_dev, NULL,
+					       &ipv6_hdr(skb)->saddr);
 		if (!neigh) {
 			net_err_ratelimited("RA: %s got default router without neighbour\n",
 					    __func__);
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index a76869ff87cd..8baac4d85251 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -206,10 +206,10 @@ static inline const void *choose_neigh_daddr(const struct in6_addr *p,
 	return daddr;
 }
 
-struct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,
-				   struct net_device *dev,
-				   struct sk_buff *skb,
-				   const void *daddr)
+struct neighbour *__ip6_dst_neigh_lookup(const struct in6_addr *gw,
+					 struct net_device *dev,
+					 struct sk_buff *skb,
+					 const void *daddr)
 {
 	struct neighbour *n;
 
@@ -228,8 +228,8 @@ static struct neighbour *ip6_dst_neigh_lookup(const struct dst_entry *dst,
 {
 	const struct rt6_info *rt = dst_rt6_info(dst);
 
-	return ip6_neigh_lookup(rt6_nexthop(rt, &in6addr_any),
-				dst_dev(dst), skb, daddr);
+	return __ip6_dst_neigh_lookup(rt6_nexthop(rt, &in6addr_any),
+				      dst_dev(dst), skb, daddr);
 }
 
 static void ip6_confirm_neigh(const struct dst_entry *dst, const void *daddr)
-- 
2.56.0.rc1.315.gc6ed9934b7-goog


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 net-next 3/9] ipv4: Add wrappers for neigh_lookup() and neigh_create().
  2026-10-03 21:23 [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Kuniyuki Iwashima
  2026-10-03 21:23 ` [PATCH v2 net-next 1/9] ipv4: Rename ipv4_neigh_lookup() to ipv4_dst_neigh_lookup() Kuniyuki Iwashima
  2026-10-03 21:23 ` [PATCH v2 net-next 2/9] ipv6: Rename ip6_neigh_lookup() to __ip6_dst_neigh_lookup() Kuniyuki Iwashima
@ 2026-10-03 21:23 ` Kuniyuki Iwashima
  2026-10-03 21:23 ` [PATCH v2 net-next 4/9] ipv6: Add wrappers for neigh_lookup() and (__)?neigh_create() Kuniyuki Iwashima
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-03 21:23 UTC (permalink / raw)
  To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Ahern, Ido Schimmel
  Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev

neigh_lookup() accepts struct neigh_table *tbl and struct
net_device *dev, and both of them must exist in the same
netns.

It is error-prone to require callers to fetch a netns and
get neigh_table, which might belong to a different netns
than dev_net(dev).

Let's add IPv4-specific wrappers for neigh_lookup() and
neigh_create() that always fetch netns from dev.

Note that we do not add wrappers for __neigh_lookup() and
__neigh_lookup_errno(), which we will remove later.

__neigh_lookup_errno() is a simple combo of neigh_lookup()
and neigh_create().  __neigh_lookup() is almost the same
but converts errno from neigh_create() to NULL.

So both names are confusing.

For IPv4, __neigh_lookup_errno() is only used in arp_req_set()
and is not worth a new wrapper.  Instead, it should be inlined.

arp_process() uses __neigh_lookup(create=0) but it should
simply be neigh_lookup(), and then __neigh_lookup(create=1)
is only used in arp_process(), so this should be inlined too.

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 include/net/arp.h | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/include/net/arp.h b/include/net/arp.h
index e932def63d62..329a5456987a 100644
--- a/include/net/arp.h
+++ b/include/net/arp.h
@@ -51,6 +51,22 @@ static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32
 	return n;
 }
 
+static inline struct neighbour *ipv4_neigh_lookup(struct net_device *dev,
+						  const void *pkey)
+{
+	struct neigh_table *tbl = arp_table(dev_net(dev));
+
+	return neigh_lookup(tbl, pkey, dev);
+}
+
+static inline struct neighbour *ipv4_neigh_create(struct net_device *dev,
+						  const void *pkey)
+{
+	struct neigh_table *tbl = arp_table(dev_net(dev));
+
+	return neigh_create(tbl, pkey, dev);
+}
+
 static inline void __ipv4_confirm_neigh(struct net_device *dev, u32 key)
 {
 	struct neighbour *n;
-- 
2.56.0.rc1.315.gc6ed9934b7-goog


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 net-next 4/9] ipv6: Add wrappers for neigh_lookup() and (__)?neigh_create().
  2026-10-03 21:23 [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Kuniyuki Iwashima
                   ` (2 preceding siblings ...)
  2026-10-03 21:23 ` [PATCH v2 net-next 3/9] ipv4: Add wrappers for neigh_lookup() and neigh_create() Kuniyuki Iwashima
@ 2026-10-03 21:23 ` Kuniyuki Iwashima
  2026-10-03 21:23 ` [PATCH v2 net-next 5/9] mlxsw: spectrum: Resolve neigh_table right before neigh_lookup() Kuniyuki Iwashima
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-03 21:23 UTC (permalink / raw)
  To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Ahern, Ido Schimmel
  Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev

neigh_lookup() accepts struct neigh_table *tbl and struct
net_device *dev, and both of them must exist in the same
netns.

It is error-prone to require callers to fetch a netns and
get neigh_table, which might belong to a different netns
than dev_net(dev).

Let's add IPv6-specific wrappers for neigh_lookup() and
(__)?neigh_create() that always fetch netns from dev.

Similar to IPv4, we do not add wrappers for __neigh_lookup()
since 3 out of 4 users call it with create=1, which should be
simply written as neigh_lookup() and neigh_create().

IPv6 has 3 callers of __neigh_create(want_ref=false), so
ipv6_neigh_create_noref() is also added.

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 include/net/ndisc.h | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 96e3bb6e83af..2fce6fccf55a 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -381,6 +381,30 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons
 	return n;
 }
 
+static inline struct neighbour *ipv6_neigh_lookup(struct net_device *dev,
+						  const void *pkey)
+{
+	struct neigh_table *tbl = nd_table(dev_net(dev));
+
+	return neigh_lookup(tbl, pkey, dev);
+}
+
+static inline struct neighbour *ipv6_neigh_create(struct net_device *dev,
+						  const void *pkey)
+{
+	struct neigh_table *tbl = nd_table(dev_net(dev));
+
+	return neigh_create(tbl, pkey, dev);
+}
+
+static inline struct neighbour *ipv6_neigh_create_noref(struct net_device *dev,
+							const void *pkey)
+{
+	struct neigh_table *tbl = nd_table(dev_net(dev));
+
+	return __neigh_create(tbl, pkey, dev, false);
+}
+
 static inline void __ipv6_confirm_neigh(struct net_device *dev,
 					const void *pkey)
 {
-- 
2.56.0.rc1.315.gc6ed9934b7-goog


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 net-next 5/9] mlxsw: spectrum: Resolve neigh_table right before neigh_lookup().
  2026-10-03 21:23 [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Kuniyuki Iwashima
                   ` (3 preceding siblings ...)
  2026-10-03 21:23 ` [PATCH v2 net-next 4/9] ipv6: Add wrappers for neigh_lookup() and (__)?neigh_create() Kuniyuki Iwashima
@ 2026-10-03 21:23 ` Kuniyuki Iwashima
  2026-10-03 21:23 ` [PATCH v2 net-next 6/9] ipv6: Use ipv6_neigh_lookup() and friends Kuniyuki Iwashima
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-03 21:23 UTC (permalink / raw)
  To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Ahern, Ido Schimmel
  Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev,
	Petr Machata

We will replace neigh_lookup() and neigh_create() with
IPv4/IPv6-specific helpers.

mlxsw has two paths where neigh_table is fetched in advance
and passed down to a function which calls neigh_lookup() and
neigh_create().

To keep the changes simple, let's prepare them beforehand
by delaying the neigh_table resolution until right before
neigh_lookup().

struct mlxsw_sp_nexthop caches neigh_tbl, so it is replaced with
family, and a new helper, mlxsw_sp_nexthop_neigh_lookup(),
resolves neigh_table and calls neigh_lookup() and neigh_create().

Similarly, mlxsw_sp_span_entry_gretap[46]_parms() now pass family
to mlxsw_sp_span_dmac() to resolve neigh_table there just before
neigh_lookup() and neigh_create().

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
Cc: Petr Machata <petrm@nvidia.com>
---
 .../ethernet/mellanox/mlxsw/spectrum_router.c | 65 ++++++++++++-------
 .../ethernet/mellanox/mlxsw/spectrum_span.c   | 31 +++++----
 2 files changed, 58 insertions(+), 38 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index f4a435885ba4..ba8a7a44ce9e 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -3072,9 +3072,9 @@ struct mlxsw_sp_nexthop {
 						   * this nexthop belongs to
 						   */
 	struct rhash_head ht_node;
-	struct neigh_table *neigh_tbl;
 	struct mlxsw_sp_nexthop_key key;
 	unsigned char gw_addr[sizeof(struct in6_addr)];
+	int family;
 	int ifindex;
 	int nh_weight;
 	int norm_nh_weight;
@@ -4300,28 +4300,49 @@ static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
 	nh->update = 1;
 }
 
+static struct neighbour *
+mlxsw_sp_nexthop_neigh_lookup(struct mlxsw_sp_nexthop *nh)
+{
+	struct neigh_table *tbl;
+	struct net_device *dev;
+	struct neighbour *n;
+	struct net *net;
+
+	dev = mlxsw_sp_nexthop_dev(nh);
+	net = dev_net(dev);
+
+#if IS_ENABLED(CONFIG_IPV6)
+	if (nh->family == AF_INET6)
+		tbl = nd_table(net);
+	else
+#endif
+		tbl = arp_table(net);
+
+	n = neigh_lookup(tbl, &nh->gw_addr, dev);
+	if (!n) {
+		n = neigh_create(tbl, &nh->gw_addr, dev);
+		if (!IS_ERR(n))
+			neigh_event_send(n, NULL);
+	}
+
+	return n;
+}
+
 static int
 mlxsw_sp_nexthop_dead_neigh_replace(struct mlxsw_sp *mlxsw_sp,
 				    struct mlxsw_sp_neigh_entry *neigh_entry)
 {
 	struct neighbour *n, *old_n = neigh_entry->key.n;
 	struct mlxsw_sp_nexthop *nh;
-	struct net_device *dev;
 	bool entry_connected;
 	u8 nud_state, dead;
 	int err;
 
 	nh = list_first_entry(&neigh_entry->nexthop_list,
 			      struct mlxsw_sp_nexthop, neigh_list_node);
-	dev = mlxsw_sp_nexthop_dev(nh);
-
-	n = neigh_lookup(nh->neigh_tbl, &nh->gw_addr, dev);
-	if (!n) {
-		n = neigh_create(nh->neigh_tbl, &nh->gw_addr, dev);
-		if (IS_ERR(n))
-			return PTR_ERR(n);
-		neigh_event_send(n, NULL);
-	}
+	n = mlxsw_sp_nexthop_neigh_lookup(nh);
+	if (IS_ERR(n))
+		return PTR_ERR(n);
 
 	mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
 	neigh_entry->key.n = n;
@@ -4402,7 +4423,6 @@ static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
 				       struct mlxsw_sp_nexthop *nh)
 {
 	struct mlxsw_sp_neigh_entry *neigh_entry;
-	struct net_device *dev;
 	struct neighbour *n;
 	u8 nud_state, dead;
 	int err;
@@ -4412,20 +4432,16 @@ static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp,
 
 	if (!nh->nhgi->gateway || nh->neigh_entry)
 		return 0;
-	dev = mlxsw_sp_nexthop_dev(nh);
 
 	/* Take a reference of neigh here ensuring that neigh would
 	 * not be destructed before the nexthop entry is finished.
 	 * The reference is taken either in neigh_lookup() or
 	 * in neigh_create() in case n is not found.
 	 */
-	n = neigh_lookup(nh->neigh_tbl, &nh->gw_addr, dev);
-	if (!n) {
-		n = neigh_create(nh->neigh_tbl, &nh->gw_addr, dev);
-		if (IS_ERR(n))
-			return PTR_ERR(n);
-		neigh_event_send(n, NULL);
-	}
+	n = mlxsw_sp_nexthop_neigh_lookup(nh);
+	if (IS_ERR(n))
+		return PTR_ERR(n);
+
 	neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, n);
 	if (!neigh_entry) {
 		neigh_entry = mlxsw_sp_neigh_entry_create(mlxsw_sp, n);
@@ -4630,7 +4646,7 @@ static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
 	nh->nh_weight = 1;
 #endif
 	memcpy(&nh->gw_addr, &fib_nh->fib_nh_gw4, sizeof(fib_nh->fib_nh_gw4));
-	nh->neigh_tbl = arp_table(mlxsw_sp_net(mlxsw_sp));
+	nh->family = AF_INET;
 	err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
 	if (err)
 		return err;
@@ -5120,7 +5136,6 @@ mlxsw_sp_nexthop_obj_init(struct mlxsw_sp *mlxsw_sp,
 			  struct nh_notifier_single_info *nh_obj, int weight)
 {
 	struct net_device *dev = nh_obj->dev;
-	struct net *net = dev_net(dev);
 	int err;
 
 	nh->nhgi = nh_grp->nhgi;
@@ -5129,12 +5144,12 @@ mlxsw_sp_nexthop_obj_init(struct mlxsw_sp *mlxsw_sp,
 	switch (nh_obj->gw_family) {
 	case AF_INET:
 		memcpy(&nh->gw_addr, &nh_obj->ipv4, sizeof(nh_obj->ipv4));
-		nh->neigh_tbl = arp_table(net);
+		nh->family = AF_INET;
 		break;
 	case AF_INET6:
 		memcpy(&nh->gw_addr, &nh_obj->ipv6, sizeof(nh_obj->ipv6));
 #if IS_ENABLED(CONFIG_IPV6)
-		nh->neigh_tbl = nd_table(net);
+		nh->family = AF_INET6;
 #endif
 		break;
 	}
@@ -6990,7 +7005,7 @@ static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
 	nh->nh_weight = rt->fib6_nh->fib_nh_weight;
 	memcpy(&nh->gw_addr, &rt->fib6_nh->fib_nh_gw6, sizeof(nh->gw_addr));
 #if IS_ENABLED(CONFIG_IPV6)
-	nh->neigh_tbl = nd_table(mlxsw_sp_net(mlxsw_sp));
+	nh->family = AF_INET6;
 #endif
 
 	err = mlxsw_sp_nexthop_counter_enable(mlxsw_sp, nh);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
index 1cd8347c274d..d34d2040177b 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
@@ -224,14 +224,24 @@ struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_phys = {
 	.deconfigure = mlxsw_sp_span_entry_phys_deconfigure,
 };
 
-static int mlxsw_sp_span_dmac(struct neigh_table *tbl,
+static int mlxsw_sp_span_dmac(int family,
 			      const void *pkey,
 			      struct net_device *dev,
 			      unsigned char dmac[ETH_ALEN])
 {
-	struct neighbour *neigh = neigh_lookup(tbl, pkey, dev);
+	struct net *net = dev_net(dev);
+	struct neigh_table *tbl;
+	struct neighbour *neigh;
 	int err = 0;
 
+#if IS_ENABLED(CONFIG_IPV6_GRE)
+	if (family == AF_INET6)
+		tbl = nd_table(net);
+	else
+#endif
+		tbl = arp_table(net);
+
+	neigh = neigh_lookup(tbl, pkey, dev);
 	if (!neigh) {
 		neigh = neigh_create(tbl, pkey, dev);
 		if (IS_ERR(neigh))
@@ -355,8 +365,7 @@ mlxsw_sp_span_entry_tunnel_parms_common(struct net_device *edev,
 					union mlxsw_sp_l3addr saddr,
 					union mlxsw_sp_l3addr daddr,
 					union mlxsw_sp_l3addr gw,
-					__u8 ttl,
-					struct neigh_table *tbl,
+					__u8 ttl, int family,
 					struct mlxsw_sp_span_parms *sparmsp)
 {
 	unsigned char dmac[ETH_ALEN];
@@ -365,7 +374,7 @@ mlxsw_sp_span_entry_tunnel_parms_common(struct net_device *edev,
 	if (mlxsw_sp_l3addr_is_zero(gw))
 		gw = daddr;
 
-	if (!edev || mlxsw_sp_span_dmac(tbl, &gw, edev, dmac))
+	if (!edev || mlxsw_sp_span_dmac(family, &gw, edev, dmac))
 		goto unoffloadable;
 
 	if (is_vlan_dev(edev))
@@ -456,7 +465,6 @@ mlxsw_sp_span_entry_gretap4_parms(struct mlxsw_sp *mlxsw_sp,
 	bool inherit_tos = tparm.iph.tos & 0x1;
 	bool inherit_ttl = !tparm.iph.ttl;
 	union mlxsw_sp_l3addr gw = daddr;
-	struct neigh_table *tbl = NULL;
 	struct net_device *l3edev;
 
 	if (!(to_dev->flags & IFF_UP) ||
@@ -470,11 +478,10 @@ mlxsw_sp_span_entry_gretap4_parms(struct mlxsw_sp *mlxsw_sp,
 		return mlxsw_sp_span_entry_unoffloadable(sparmsp);
 
 	l3edev = mlxsw_sp_span_gretap4_route(to_dev, &saddr.addr4, &gw.addr4);
-	if (l3edev)
-		tbl = arp_table(dev_net(l3edev));
+
 	return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
 						       tparm.iph.ttl,
-						       tbl, sparmsp);
+						       AF_INET, sparmsp);
 }
 
 static int
@@ -564,7 +571,6 @@ mlxsw_sp_span_entry_gretap6_parms(struct mlxsw_sp *mlxsw_sp,
 	union mlxsw_sp_l3addr daddr = { .addr6 = tparm.raddr };
 	bool inherit_ttl = !tparm.hop_limit;
 	union mlxsw_sp_l3addr gw = daddr;
-	struct neigh_table *tbl = NULL;
 	struct net_device *l3edev;
 
 	if (!(to_dev->flags & IFF_UP) ||
@@ -578,11 +584,10 @@ mlxsw_sp_span_entry_gretap6_parms(struct mlxsw_sp *mlxsw_sp,
 		return mlxsw_sp_span_entry_unoffloadable(sparmsp);
 
 	l3edev = mlxsw_sp_span_gretap6_route(to_dev, &saddr.addr6, &gw.addr6);
-	if (l3edev)
-		tbl = nd_table(dev_net(l3edev));
+
 	return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
 						       tparm.hop_limit,
-						       tbl, sparmsp);
+						       AF_INET6, sparmsp);
 }
 
 static int
-- 
2.56.0.rc1.315.gc6ed9934b7-goog


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 net-next 6/9] ipv6: Use ipv6_neigh_lookup() and friends.
  2026-10-03 21:23 [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Kuniyuki Iwashima
                   ` (4 preceding siblings ...)
  2026-10-03 21:23 ` [PATCH v2 net-next 5/9] mlxsw: spectrum: Resolve neigh_table right before neigh_lookup() Kuniyuki Iwashima
@ 2026-10-03 21:23 ` Kuniyuki Iwashima
  2026-10-06 17:53   ` Fernando Fernandez Mancera
  2026-10-03 21:23 ` [PATCH v2 net-next 7/9] ipv4: Use ipv4_neigh_lookup() " Kuniyuki Iwashima
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-03 21:23 UTC (permalink / raw)
  To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Ahern, Ido Schimmel
  Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev,
	syzbot+5a8857f0b4a0a7b12c62, Saeed Mahameed, Leon Romanovsky,
	Tariq Toukan, Mark Bloch, Petr Machata, Edward Cree,
	Nikolay Aleksandrov, Alexander Aring, Stefan Schmidt,
	Miquel Raynal

syzbot reported the splat below in neigh_mark_dead() [0]
where tbl->lock was not held while neigh_ifdown() should
have acquired one.

This only happens when a neigh_table accidentally has a
neighbour from a different netns.

In ip6_finish_output2(), the net argument is the caller's and
does not always match dev_net(dev) fetched from dst. (e.g. xfrm)

It is error-prone to require callers to fetch netns and
neigh_table and pass it with dev to neigh_lookup(), etc.

Let's replace neigh_lookup() and friends with IPv6 helpers.

Note that __neigh_lookup() is split into ipv6_neigh_lookup()
and ipv6_neigh_create().

[0]:
debug_locks && !(lock_is_held(&(&n->tbl->lock)->dep_map) != 0)
WARNING: net/core/neighbour.c:154 at neigh_mark_dead+0x2fb/0x340 net/core/neighbour.c:154, CPU#0: syz.0.857/9765
Modules linked in:
CPU: 0 UID: 0 PID: 9765 Comm: syz.0.857 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/24/2026
RIP: 0010:neigh_mark_dead+0x2fb/0x340 net/core/neighbour.c:154
Code: e8 03 42 80 3c 28 00 74 08 48 89 df e8 ee 9a 80 f8 4c 89 33 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc e8 86 95 10 f8 90 <0f> 0b 90 e9 8f fd ff ff 48 c7 c1 80 9a 7a 90 80 e1 07 80 c1 03 38
RSP: 0018:ffffc90003726600 EFLAGS: 00010287
RAX: ffffffff89b7467a RBX: ffff888022e86000 RCX: 0000000000080000
RDX: ffffc900047fa000 RSI: 0000000000003e08 RDI: 0000000000003e09
RBP: 0000000000000000 R08: 0000000000000003 R09: 0000000000000004
R10: dffffc0000000000 R11: fffff520006e4cb8 R12: ffff888022e8613c
R13: dffffc0000000000 R14: ffff888022e86020 R15: ffff888022e86000
FS:  00007f9c30f046c0(0000) GS:ffff888124cc6000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000000 CR3: 0000000076c56000 CR4: 00000000003526f0
Call Trace:
 <TASK>
 neigh_flush_one+0x27c/0x5a0 net/core/neighbour.c:388
 neigh_flush_dev net/core/neighbour.c:432 [inline]
 __neigh_ifdown+0x1fb/0xc70 net/core/neighbour.c:465
 neigh_ifdown+0x1f/0x30 net/core/neighbour.c:487
 rt6_disable_ip+0x751/0x7e0 net/ipv6/route.c:5058
 addrconf_ifdown+0x155/0x1ad0 net/ipv6/addrconf.c:3892
 addrconf_notify+0x1bc/0x1050 net/ipv6/addrconf.c:-1
 notifier_call_chain+0x1a5/0x3d0 kernel/notifier.c:85
 call_netdevice_notifiers_mtu net/core/dev.c:2350 [inline]
 netif_set_mtu_ext+0x5ac/0x830 net/core/dev.c:9963
 do_setlink+0xa81/0x47a0 net/core/rtnetlink.c:3247
 rtnl_setlink+0x578/0x820 net/core/rtnetlink.c:3623
 rtnetlink_rcv_msg+0x802/0xc00 net/core/rtnetlink.c:7159
 netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2572
 netlink_unicast_kernel net/netlink/af_netlink.c:1335 [inline]
 netlink_unicast+0x7bd/0x940 net/netlink/af_netlink.c:1361
 netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1916
 sock_sendmsg_nosec+0x13a/0x180 net/socket.c:800
 __sock_sendmsg net/socket.c:815 [inline]
 sock_write_iter+0x2de/0x3e0 net/socket.c:1266
 do_iter_readv_writev+0x612/0x8c0 fs/read_write.c:-1
 vfs_writev+0x343/0x990 fs/read_write.c:1058
 do_writev+0x154/0x2e0 fs/read_write.c:1104
 do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
 do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f9c2ff9e159
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f9c30f04028 EFLAGS: 00000246 ORIG_RAX: 0000000000000014
RAX: ffffffffffffffda RBX: 00007f9c30225fa0 RCX: 00007f9c2ff9e159
RDX: 0000000000000001 RSI: 00002000000003c0 RDI: 0000000000000004
RBP: 00007f9c3003506b R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f9c30226038 R14: 00007f9c30225fa0 R15: 00007ffed3c74808
 </TASK>

Fixes: df5f4f372de5 ("ipv6: Replace &nd_tbl with nd_table(net).")
Reported-by: syzbot+5a8857f0b4a0a7b12c62@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/6abd69a9.80e1c6cc.22483f.0005.GAE@google.com/
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
v2: Convert __teql_resolve()

Cc: Saeed Mahameed <saeedm@nvidia.com>
Cc: Leon Romanovsky <leon@kernel.org>
Cc: Tariq Toukan <tariqt@nvidia.com>
Cc: Mark Bloch <mbloch@nvidia.com>
Cc: Petr Machata <petrm@nvidia.com>
Cc: Edward Cree <ecree.xilinx@gmail.com>
Cc: Nikolay Aleksandrov <razor@blackwall.org>
Cc: Alexander Aring <alex.aring@gmail.com>
Cc: Stefan Schmidt <stefan@datenfreihafen.org>
Cc: Miquel Raynal <miquel.raynal@bootlin.com>
---
 .../mellanox/mlx5/core/en/tc_tun_encap.c      | 13 +++----
 .../ethernet/mellanox/mlxsw/spectrum_router.c | 29 ++++++++-------
 .../ethernet/mellanox/mlxsw/spectrum_span.c   | 24 ++++++++-----
 .../netronome/nfp/flower/tunnel_conf.c        |  4 +--
 drivers/net/ethernet/sfc/tc_counters.c        |  5 ++-
 drivers/net/vrf.c                             |  4 +--
 drivers/net/vxlan/vxlan_core.c                |  6 ++--
 include/net/ndisc.h                           |  7 ++--
 net/bridge/br_arp_nd_proxy.c                  |  2 +-
 net/ieee802154/6lowpan/tx.c                   |  3 +-
 net/ipv4/fib_semantics.c                      |  2 +-
 net/ipv6/ip6_output.c                         |  2 +-
 net/ipv6/ndisc.c                              | 36 ++++++++++++-------
 net/ipv6/route.c                              | 11 +++---
 net/sched/sch_teql.c                          | 12 ++++++-
 15 files changed, 90 insertions(+), 70 deletions(-)

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
index 67c12ca19d59..fe0258375ef6 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
@@ -439,21 +439,18 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
 	if (neigh_used) {
 		struct net_device *dev = READ_ONCE(nhe->neigh_dev);
 		struct net *net = dev_net(dev);
-		struct neigh_table *tbl;
 
 		nhe->reported_lastuse = jiffies;
 
+		/* find the relevant neigh according to the cached device and
+		 * dst ip pair
+		 */
 #if IS_ENABLED(CONFIG_IPV6)
 		if (m_neigh->family != AF_INET)
-			tbl = nd_table(net);
+			n = ipv6_neigh_lookup(dev, &m_neigh->dst_ip);
 		else
 #endif
-			tbl = arp_table(net);
-
-		/* find the relevant neigh according to the cached device and
-		 * dst ip pair
-		 */
-		n = neigh_lookup(tbl, &m_neigh->dst_ip, dev);
+			n = neigh_lookup(arp_table(net), &m_neigh->dst_ip, dev);
 		if (!n)
 			return;
 
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index ba8a7a44ce9e..1f4753213b9c 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -2449,8 +2449,6 @@ static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
 						   char *rauhtd_pl,
 						   int rec_index)
 {
-	struct net *net = mlxsw_sp_net(mlxsw_sp);
-	struct neigh_table *tbl;
 	struct net_device *dev;
 	struct neighbour *n;
 	struct in6_addr dip;
@@ -2464,9 +2462,8 @@ static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
 		return;
 	}
 
-	tbl = nd_table(net);
 	dev = mlxsw_sp_rif_dev(mlxsw_sp->router->rifs[rif]);
-	n = neigh_lookup(tbl, &dip, dev);
+	n = ipv6_neigh_lookup(dev, &dip);
 	if (!n)
 		return;
 
@@ -4312,17 +4309,23 @@ mlxsw_sp_nexthop_neigh_lookup(struct mlxsw_sp_nexthop *nh)
 	net = dev_net(dev);
 
 #if IS_ENABLED(CONFIG_IPV6)
-	if (nh->family == AF_INET6)
-		tbl = nd_table(net);
-	else
+	if (nh->family == AF_INET6) {
+		n = ipv6_neigh_lookup(dev, &nh->gw_addr);
+		if (!n) {
+			n = ipv6_neigh_create(dev, &nh->gw_addr);
+			if (!IS_ERR(n))
+				neigh_event_send(n, NULL);
+		}
+	} else
 #endif
+	{
 		tbl = arp_table(net);
-
-	n = neigh_lookup(tbl, &nh->gw_addr, dev);
-	if (!n) {
-		n = neigh_create(tbl, &nh->gw_addr, dev);
-		if (!IS_ERR(n))
-			neigh_event_send(n, NULL);
+		n = neigh_lookup(tbl, &nh->gw_addr, dev);
+		if (!n) {
+			n = neigh_create(tbl, &nh->gw_addr, dev);
+			if (!IS_ERR(n))
+				neigh_event_send(n, NULL);
+		}
 	}
 
 	return n;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
index d34d2040177b..79947f68b10d 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
@@ -235,17 +235,23 @@ static int mlxsw_sp_span_dmac(int family,
 	int err = 0;
 
 #if IS_ENABLED(CONFIG_IPV6_GRE)
-	if (family == AF_INET6)
-		tbl = nd_table(net);
-	else
+	if (family == AF_INET6) {
+		neigh = ipv6_neigh_lookup(dev, pkey);
+		if (!neigh) {
+			neigh = ipv6_neigh_create(dev, pkey);
+			if (IS_ERR(neigh))
+				return PTR_ERR(neigh);
+		}
+	} else
 #endif
+	{
 		tbl = arp_table(net);
-
-	neigh = neigh_lookup(tbl, pkey, dev);
-	if (!neigh) {
-		neigh = neigh_create(tbl, pkey, dev);
-		if (IS_ERR(neigh))
-			return PTR_ERR(neigh);
+		neigh = neigh_lookup(tbl, pkey, dev);
+		if (!neigh) {
+			neigh = neigh_create(tbl, pkey, dev);
+			if (IS_ERR(neigh))
+				return PTR_ERR(neigh);
+		}
 	}
 
 	neigh_event_send(neigh, NULL);
diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
index d650d33e3709..27d80ec8e895 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
@@ -253,7 +253,6 @@ void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb)
 #if IS_ENABLED(CONFIG_IPV6)
 	struct nfp_tun_active_tuns_v6 *payload;
 	struct net_device *netdev;
-	struct neigh_table *tbl;
 	int count, i, pay_len;
 	struct neighbour *n;
 	void *ipv6_add;
@@ -280,8 +279,7 @@ void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb)
 		if (!netdev)
 			continue;
 
-		tbl = nd_table(dev_net(netdev));
-		n = neigh_lookup(tbl, ipv6_add, netdev);
+		n = ipv6_neigh_lookup(netdev, ipv6_add);
 		if (!n)
 			continue;
 
diff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c
index 793a562fc1f7..ae6cc6b93864 100644
--- a/drivers/net/ethernet/sfc/tc_counters.c
+++ b/drivers/net/ethernet/sfc/tc_counters.c
@@ -116,9 +116,8 @@ static void efx_tc_counter_work(struct work_struct *work)
 					 encap->neigh->egdev);
 		else
 #if IS_ENABLED(CONFIG_IPV6)
-			n = neigh_lookup(nd_table(net),
-					 &encap->neigh->dst_ip6,
-					 encap->neigh->egdev);
+			n = ipv6_neigh_lookup(encap->neigh->egdev,
+					      &encap->neigh->dst_ip6);
 #else
 			n = NULL;
 #endif
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 320f3584a43b..79d433f49f80 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -614,9 +614,9 @@ static int vrf_finish_output6(struct net *net, struct sock *sk,
 
 	rcu_read_lock();
 	nexthop = rt6_nexthop(dst_rt6_info(dst), &ipv6_hdr(skb)->daddr);
-	neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
+	neigh = __ipv6_neigh_lookup_noref(dev, nexthop);
 	if (unlikely(!neigh))
-		neigh = __neigh_create(nd_table(net), nexthop, dst->dev, false);
+		neigh = ipv6_neigh_create_noref(dev, nexthop);
 	if (!IS_ERR(neigh)) {
 		sock_confirm_neigh(skb, neigh);
 		ret = neigh_output(neigh, skb, false);
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 27b0b6567d52..513779c07621 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -2091,8 +2091,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb,
 	    ipv6_addr_is_multicast(&msg->target))
 		goto out;
 
-	n = neigh_lookup(nd_table(dev_net(dev)), &msg->target, dev);
-
+	n = ipv6_neigh_lookup(dev, &msg->target);
 	if (n) {
 		struct vxlan_rdst *rdst = NULL;
 		u8 ha[ETH_ALEN] __aligned(2);
@@ -2189,9 +2188,8 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb,
 		if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
 			return false;
 
-		tbl = nd_table(dev_net(dev));
 		pip6 = ipv6_hdr(skb);
-		n = neigh_lookup(tbl, &pip6->daddr, dev);
+		n = ipv6_neigh_lookup(dev, &pip6->daddr);
 		if (!n && (cfg->flags & VXLAN_F_L3MISS)) {
 			union vxlan_addr ipa = {
 				.sin6.sin6_addr = pip6->daddr,
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 2fce6fccf55a..91898a2475e7 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -423,11 +423,8 @@ static inline struct neighbour *ip_neigh_gw6(struct net_device *dev,
 	struct neighbour *neigh;
 
 	neigh = __ipv6_neigh_lookup_noref(dev, addr);
-	if (unlikely(!neigh)) {
-		struct neigh_table *tbl = nd_table(dev_net(dev));
-
-		neigh = __neigh_create(tbl, addr, dev, false);
-	}
+	if (unlikely(!neigh))
+		neigh = ipv6_neigh_create_noref(dev, addr);
 
 	return neigh;
 #else
diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
index ba4a63840b21..c23b99517cd5 100644
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -487,7 +487,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
 		return;
 	}
 
-	n = neigh_lookup(nd_table(dev_net(vlandev)), &msg->target, vlandev);
+	n = ipv6_neigh_lookup(vlandev, &msg->target);
 	if (n) {
 		struct net_bridge_fdb_entry *f;
 		u8 ha[ETH_ALEN] __aligned(2);
diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c
index 4f511476b992..b261daedc290 100644
--- a/net/ieee802154/6lowpan/tx.c
+++ b/net/ieee802154/6lowpan/tx.c
@@ -58,9 +58,8 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
 		info->daddr.mode = IEEE802154_ADDR_SHORT;
 	} else {
 		__le16 short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
-		struct neigh_table *tbl = nd_table(dev_net(ldev));
 
-		n = neigh_lookup(tbl, &hdr->daddr, ldev);
+		n = ipv6_neigh_lookup(ldev, &hdr->daddr);
 		if (n) {
 			llneigh = lowpan_802154_neigh(neighbour_priv(n));
 			read_lock_bh(&n->lock);
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index e3bcc25229b0..a98c7670d2ce 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -617,7 +617,7 @@ static int fib_detect_death(struct fib_info *fi, int order,
 	if (likely(nhc->nhc_gw_family == AF_INET))
 		n = neigh_lookup(arp_table(net), &nhc->nhc_gw.ipv4, nhc->nhc_dev);
 	else if (IS_ENABLED(CONFIG_IPV6) && nhc->nhc_gw_family == AF_INET6)
-		n = neigh_lookup(nd_table(net), &nhc->nhc_gw.ipv6, nhc->nhc_dev);
+		n = ipv6_neigh_lookup(nhc->nhc_dev, &nhc->nhc_gw.ipv6);
 	else
 		n = NULL;
 
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 10146a57b69e..25fe0ba98b1a 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -127,7 +127,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
 
 	if (IS_ERR_OR_NULL(neigh)) {
 		if (unlikely(!neigh))
-			neigh = __neigh_create(nd_table(net), nexthop, dev, false);
+			neigh = ipv6_neigh_create_noref(dev, nexthop);
 		if (IS_ERR(neigh)) {
 			IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTNOROUTES);
 			kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_CREATEFAIL);
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index e1cbffaa0ad0..0ed1a619372b 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -947,8 +947,12 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
 	 *	update / create cache entry
 	 *	for the source address
 	 */
-	neigh = __neigh_lookup(tbl, saddr, dev,
-			       !inc || lladdr || !dev->addr_len);
+	neigh = ipv6_neigh_lookup(dev, saddr);
+	if (!neigh && (!inc || lladdr || !dev->addr_len)) {
+		neigh = ipv6_neigh_create(dev, saddr);
+		if (IS_ERR(neigh))
+			neigh = NULL;
+	}
 	if (neigh)
 		ndisc_update(dev, neigh, lladdr, NUD_STALE,
 			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
@@ -998,7 +1002,6 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
 	struct net *net = dev_net(dev);
 	struct ndisc_options ndopts;
 	struct inet6_ifaddr *ifp;
-	struct neigh_table *tbl;
 	struct neighbour *neigh;
 	struct inet6_dev *idev;
 	u8 *lladdr = NULL;
@@ -1063,8 +1066,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
 		return reason;
 	}
 
-	tbl = nd_table(net);
-	neigh = neigh_lookup(tbl, &msg->target, dev);
+	neigh = ipv6_neigh_lookup(dev, &msg->target);
 
 	/* RFC 9131 updates original Neighbour Discovery RFC 4861.
 	 * NAs with Target LL Address option can now create a STALE neighbor
@@ -1093,7 +1095,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
 			return reason;
 		}
 		if (!neigh)
-			neigh = neigh_create(tbl, &msg->target, dev);
+			neigh = ipv6_neigh_create(dev, &msg->target);
 		new_state = NUD_STALE;
 	}
 
@@ -1108,7 +1110,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(tbl, &msg->target, dev)) {
+		    pneigh_lookup(nd_table(dev_net(dev)), &msg->target, dev)) {
 			/* XXX: idev->cnf.proxy_ndp */
 			goto out;
 		}
@@ -1141,7 +1143,6 @@ static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
 	unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
 	struct net_device *dev = skb->dev;
 	struct ndisc_options ndopts;
-	struct neigh_table *tbl;
 	struct neighbour *neigh;
 	struct inet6_dev *idev;
 	u8 *lladdr = NULL;
@@ -1177,8 +1178,12 @@ static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
 			goto out;
 	}
 
-	tbl = nd_table(dev_net(dev));
-	neigh = __neigh_lookup(tbl, saddr, dev, 1);
+	neigh = ipv6_neigh_lookup(dev, saddr);
+	if (!neigh) {
+		neigh = ipv6_neigh_create(dev, saddr);
+		if (IS_ERR(neigh))
+			goto out;
+	}
 	if (neigh) {
 		ndisc_update(dev, neigh, lladdr, NUD_STALE,
 			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
@@ -1478,9 +1483,14 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
 	 *	Process options.
 	 */
 
-	if (!neigh)
-		neigh = __neigh_lookup(nd_table(net), &ipv6_hdr(skb)->saddr,
-				       skb->dev, 1);
+	if (!neigh) {
+		neigh = ipv6_neigh_lookup(skb->dev, &ipv6_hdr(skb)->saddr);
+		if (!neigh) {
+			neigh = ipv6_neigh_create(skb->dev, &ipv6_hdr(skb)->saddr);
+			if (IS_ERR(neigh))
+				neigh = NULL;
+		}
+	}
 	if (neigh) {
 		u8 *lladdr = NULL;
 		if (ndopts.nd_opts_src_lladdr) {
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 8baac4d85251..ea9f0a4c34f9 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -218,7 +218,7 @@ struct neighbour *__ip6_dst_neigh_lookup(const struct in6_addr *gw,
 	if (n)
 		return n;
 
-	n = neigh_create(nd_table(dev_net(dev)), daddr, dev);
+	n = ipv6_neigh_create(dev, daddr);
 	return IS_ERR(n) ? NULL : n;
 }
 
@@ -4277,9 +4277,12 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
 	 */
 	dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
 
-	neigh = __neigh_lookup(nd_table(dev_net(dev)), &msg->target, dev, 1);
-	if (!neigh)
-		return;
+	neigh = ipv6_neigh_lookup(dev, &msg->target);
+	if (!neigh) {
+		neigh = ipv6_neigh_create(dev, &msg->target);
+		if (IS_ERR(neigh))
+			return;
+	}
 
 	/*
 	 *	We have finally decided to accept it.
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index 409ce50cc0db..19ccf1a55988 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -16,6 +16,7 @@
 #include <linux/skbuff.h>
 #include <linux/moduleparam.h>
 #include <net/dst.h>
+#include <net/ndisc.h>
 #include <net/neighbour.h>
 #include <net/pkt_sched.h>
 
@@ -257,7 +258,16 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res,
 	if (dst->dev != dev) {
 		struct neighbour *mn;
 
-		mn = __neigh_lookup_errno(n->tbl, n->primary_key, dev);
+#if IS_ENABLED(CONFIG_IPV6)
+		if (n->tbl->family == AF_INET6) {
+			mn = ipv6_neigh_lookup(dev, n->primary_key);
+			if (!mn)
+				mn = ipv6_neigh_create(dev, n->primary_key);
+		} else
+#endif
+		{
+			mn = __neigh_lookup_errno(n->tbl, n->primary_key, dev);
+		}
 		neigh_release(n);
 		if (IS_ERR(mn))
 			return PTR_ERR(mn);
-- 
2.56.0.rc1.315.gc6ed9934b7-goog


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 net-next 7/9] ipv4: Use ipv4_neigh_lookup() and friends.
  2026-10-03 21:23 [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Kuniyuki Iwashima
                   ` (5 preceding siblings ...)
  2026-10-03 21:23 ` [PATCH v2 net-next 6/9] ipv6: Use ipv6_neigh_lookup() and friends Kuniyuki Iwashima
@ 2026-10-03 21:23 ` Kuniyuki Iwashima
  2026-10-03 21:23 ` [PATCH v2 net-next 8/9] neighbour: Remove __neigh_lookup() and __neigh_lookup_errno() Kuniyuki Iwashima
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-03 21:23 UTC (permalink / raw)
  To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Ahern, Ido Schimmel
  Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev,
	Elad Nachman, Saeed Mahameed, Leon Romanovsky, Tariq Toukan,
	Mark Bloch, Boris Pismenny, Petr Machata, Jiri Pirko, Edward Cree,
	Nikolay Aleksandrov

It is error-prone to require callers to fetch netns and
neigh_table and pass it with dev to neigh_lookup(), etc.

Let's replace neigh_lookup() and friends with IPv4 helpers.

Note that __neigh_lookup(create=false) is replaced with
ipv4_neigh_lookup() and __neigh_lookup(create=true) and
__neigh_lookup_errno() are split into ipv4_neigh_lookup()
and ipv4_neigh_create().

Fixes: 2430df635a59 ("ipv4: Replace &arp_tbl with arp_table(net).")
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
v2: Convert __teql_resolve()

Cc: Elad Nachman <enachman@marvell.com>
Cc: Saeed Mahameed <saeedm@nvidia.com>
Cc: Leon Romanovsky <leon@kernel.org>
Cc: Tariq Toukan <tariqt@nvidia.com>
Cc: Mark Bloch <mbloch@nvidia.com>
Cc: Boris Pismenny <borisp@nvidia.com>
Cc: Petr Machata <petrm@nvidia.com>
Cc: Jiri Pirko <jiri@resnulli.us>
Cc: Edward Cree <ecree.xilinx@gmail.com>
Cc: Nikolay Aleksandrov <razor@blackwall.org>
---
 .../marvell/prestera/prestera_router.c        | 14 ++++------
 .../mellanox/mlx5/core/en/tc_tun_encap.c      |  3 +--
 .../mellanox/mlx5/core/en_accel/ipsec.c       |  6 ++---
 .../ethernet/mellanox/mlxsw/spectrum_router.c | 13 +++-------
 .../ethernet/mellanox/mlxsw/spectrum_span.c   |  7 ++---
 .../netronome/nfp/flower/tunnel_conf.c        |  4 +--
 drivers/net/ethernet/rocker/rocker_ofdpa.c    |  2 +-
 drivers/net/ethernet/sfc/tc_counters.c        |  6 ++---
 drivers/net/vxlan/vxlan_core.c                |  8 ++----
 net/bridge/br_arp_nd_proxy.c                  |  2 +-
 net/ipv4/arp.c                                | 26 ++++++++++++-------
 net/ipv4/fib_semantics.c                      |  3 +--
 net/ipv4/route.c                              |  2 +-
 net/sched/sch_teql.c                          |  5 +++-
 14 files changed, 42 insertions(+), 59 deletions(-)

diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c
index ba45b61b09bb..44c9c1fa3189 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_router.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_router.c
@@ -683,8 +683,7 @@ __prestera_k_arb_n_offload_set(struct prestera_switch *sw,
 {
 	struct neighbour *n;
 
-	n = neigh_lookup(arp_table(&init_net), &nc->key.addr.u.ipv4,
-			 nc->key.dev);
+	n = ipv4_neigh_lookup(nc->key.dev, &nc->key.addr.u.ipv4);
 	if (!n)
 		return;
 
@@ -790,7 +789,7 @@ __prestera_k_arb_nc_kern_n_fetch(struct prestera_switch *sw,
 	int err;
 
 	memset(&nc->nh_neigh_info, 0, sizeof(nc->nh_neigh_info));
-	n = neigh_lookup(arp_table(&init_net), &nc->key.addr.u.ipv4, nc->key.dev);
+	n = ipv4_neigh_lookup(nc->key.dev, &nc->key.addr.u.ipv4);
 	if (!n)
 		goto out;
 
@@ -1052,13 +1051,10 @@ static void __prestera_k_arb_hw_state_upd(struct prestera_switch *sw,
 #endif /* PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH */
 
 	if (nc->key.addr.v == PRESTERA_IPV4) {
-		struct neigh_table *tbl = arp_table(&init_net);
-
-		n = neigh_lookup(tbl, &nc->key.addr.u.ipv4,
-				 nc->key.dev);
+		n = ipv4_neigh_lookup(nc->key.dev, &nc->key.addr.u.ipv4);
 		if (!n)
-			n = neigh_create(tbl, &nc->key.addr.u.ipv4,
-					 nc->key.dev);
+			n = ipv4_neigh_create(nc->key.dev,
+					      &nc->key.addr.u.ipv4);
 	} else {
 		n = NULL;
 	}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
index fe0258375ef6..5e40107efa26 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
@@ -438,7 +438,6 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
 
 	if (neigh_used) {
 		struct net_device *dev = READ_ONCE(nhe->neigh_dev);
-		struct net *net = dev_net(dev);
 
 		nhe->reported_lastuse = jiffies;
 
@@ -450,7 +449,7 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
 			n = ipv6_neigh_lookup(dev, &m_neigh->dst_ip);
 		else
 #endif
-			n = neigh_lookup(arp_table(net), &m_neigh->dst_ip, dev);
+			n = ipv4_neigh_lookup(dev, &m_neigh->dst_ip);
 		if (!n)
 			return;
 
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
index 37a8ddee3ea1..16edd3263aed 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
@@ -262,7 +262,6 @@ static void mlx5e_ipsec_init_macs(struct mlx5e_ipsec_sa_entry *sa_entry,
 	struct net_device *netdev = sa_entry->dev;
 	struct xfrm_state *x = sa_entry->x;
 	struct dst_entry *rt_dst_entry;
-	struct neigh_table *tbl;
 	struct flowi4 fl4 = {};
 	struct flowi6 fl6 = {};
 	struct neighbour *n;
@@ -365,10 +364,9 @@ static void mlx5e_ipsec_init_macs(struct mlx5e_ipsec_sa_entry *sa_entry,
 	return;
 
 neigh:
-	tbl = arp_table(dev_net(netdev));
-	n = neigh_lookup(tbl, pkey, netdev);
+	n = ipv4_neigh_lookup(netdev, pkey);
 	if (!n) {
-		n = neigh_create(tbl, pkey, netdev);
+		n = ipv4_neigh_create(netdev, pkey);
 		if (IS_ERR(n))
 			return;
 		neigh_event_send(n, NULL);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index 1f4753213b9c..07d22257b703 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -2415,8 +2415,6 @@ static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
 						   int ent_index)
 {
 	u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
-	struct net *net = mlxsw_sp_net(mlxsw_sp);
-	struct neigh_table *tbl;
 	struct net_device *dev;
 	struct neighbour *n;
 	__be32 dipn;
@@ -2432,10 +2430,9 @@ static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
 		return;
 	}
 
-	tbl = arp_table(net);
 	dipn = htonl(dip);
 	dev = mlxsw_sp_rif_dev(mlxsw_sp->router->rifs[rif]);
-	n = neigh_lookup(tbl, &dipn, dev);
+	n = ipv4_neigh_lookup(dev, &dipn);
 	if (!n)
 		return;
 
@@ -4300,13 +4297,10 @@ static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
 static struct neighbour *
 mlxsw_sp_nexthop_neigh_lookup(struct mlxsw_sp_nexthop *nh)
 {
-	struct neigh_table *tbl;
 	struct net_device *dev;
 	struct neighbour *n;
-	struct net *net;
 
 	dev = mlxsw_sp_nexthop_dev(nh);
-	net = dev_net(dev);
 
 #if IS_ENABLED(CONFIG_IPV6)
 	if (nh->family == AF_INET6) {
@@ -4319,10 +4313,9 @@ mlxsw_sp_nexthop_neigh_lookup(struct mlxsw_sp_nexthop *nh)
 	} else
 #endif
 	{
-		tbl = arp_table(net);
-		n = neigh_lookup(tbl, &nh->gw_addr, dev);
+		n = ipv4_neigh_lookup(dev, &nh->gw_addr);
 		if (!n) {
-			n = neigh_create(tbl, &nh->gw_addr, dev);
+			n = ipv4_neigh_create(dev, &nh->gw_addr);
 			if (!IS_ERR(n))
 				neigh_event_send(n, NULL);
 		}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
index 79947f68b10d..0b84a25e2ea8 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
@@ -229,8 +229,6 @@ static int mlxsw_sp_span_dmac(int family,
 			      struct net_device *dev,
 			      unsigned char dmac[ETH_ALEN])
 {
-	struct net *net = dev_net(dev);
-	struct neigh_table *tbl;
 	struct neighbour *neigh;
 	int err = 0;
 
@@ -245,10 +243,9 @@ static int mlxsw_sp_span_dmac(int family,
 	} else
 #endif
 	{
-		tbl = arp_table(net);
-		neigh = neigh_lookup(tbl, pkey, dev);
+		neigh = ipv4_neigh_lookup(dev, pkey);
 		if (!neigh) {
-			neigh = neigh_create(tbl, pkey, dev);
+			neigh = ipv4_neigh_create(dev, pkey);
 			if (IS_ERR(neigh))
 				return PTR_ERR(neigh);
 		}
diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
index 27d80ec8e895..3b47e9fe64e1 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
@@ -209,7 +209,6 @@ void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb)
 {
 	struct nfp_tun_active_tuns *payload;
 	struct net_device *netdev;
-	struct neigh_table *tbl;
 	int count, i, pay_len;
 	struct neighbour *n;
 	__be32 ipv4_addr;
@@ -236,8 +235,7 @@ void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb)
 		if (!netdev)
 			continue;
 
-		tbl = arp_table(dev_net(netdev));
-		n = neigh_lookup(tbl, &ipv4_addr, netdev);
+		n = ipv4_neigh_lookup(netdev, &ipv4_addr);
 		if (!n)
 			continue;
 
diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c
index ead8b447b89c..ace69a1d5275 100644
--- a/drivers/net/ethernet/rocker/rocker_ofdpa.c
+++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c
@@ -1336,7 +1336,7 @@ static int ofdpa_port_ipv4_resolve(struct ofdpa_port *ofdpa_port,
 	int err = 0;
 
 	if (!n) {
-		n = neigh_create(arp_table(&init_net), &ip_addr, dev);
+		n = ipv4_neigh_create(dev, &ip_addr);
 		if (IS_ERR(n))
 			return PTR_ERR(n);
 	}
diff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c
index ae6cc6b93864..fcb2be6abb20 100644
--- a/drivers/net/ethernet/sfc/tc_counters.c
+++ b/drivers/net/ethernet/sfc/tc_counters.c
@@ -91,7 +91,6 @@ static void efx_tc_counter_work(struct work_struct *work)
 	struct efx_tc_action_set *act;
 	unsigned long touched;
 	struct neighbour *n;
-	struct net *net;
 
 	spin_lock_bh(&cnt->lock);
 	touched = READ_ONCE(cnt->touched);
@@ -106,14 +105,13 @@ static void efx_tc_counter_work(struct work_struct *work)
 			continue;
 
 		encap->neigh->used = touched;
-		net = encap->neigh->net;
 
 		/* We have passed traffic using this ARP entry, so
 		 * indicate to the ARP cache that it's still active
 		 */
 		if (encap->neigh->dst_ip)
-			n = neigh_lookup(arp_table(net), &encap->neigh->dst_ip,
-					 encap->neigh->egdev);
+			n = ipv4_neigh_lookup(encap->neigh->egdev,
+					      &encap->neigh->dst_ip);
 		else
 #if IS_ENABLED(CONFIG_IPV6)
 			n = ipv6_neigh_lookup(encap->neigh->egdev,
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 513779c07621..35bd92f5e460 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -1877,7 +1877,6 @@ static int vxlan_err_lookup(struct sock *sk, struct sk_buff *skb)
 static int arp_reduce(struct net_device *dev, struct sk_buff *skb,
 		      const struct vxlan_config *cfg, __be32 vni)
 {
-	struct neigh_table *tbl = arp_table(dev_net(dev));
 	struct vxlan_dev *vxlan = netdev_priv(dev);
 	struct neighbour *n;
 	struct arphdr *parp;
@@ -1914,8 +1913,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb,
 	    ipv4_is_multicast(tip))
 		goto out;
 
-	n = neigh_lookup(tbl, &tip, dev);
-
+	n = ipv4_neigh_lookup(dev, &tip);
 	if (n) {
 		struct vxlan_rdst *rdst = NULL;
 		u8 ha[ETH_ALEN] __aligned(2);
@@ -2145,7 +2143,6 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb,
 static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb,
 			       const struct vxlan_config *cfg)
 {
-	struct neigh_table *tbl;
 	struct neighbour *n;
 
 	if (is_multicast_ether_addr(eth_hdr(skb)->h_dest))
@@ -2160,9 +2157,8 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb,
 		if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
 			return false;
 
-		tbl = arp_table(dev_net(dev));
 		pip = ip_hdr(skb);
-		n = neigh_lookup(tbl, &pip->daddr, dev);
+		n = ipv4_neigh_lookup(dev, &pip->daddr);
 		if (!n && (cfg->flags & VXLAN_F_L3MISS)) {
 			union vxlan_addr ipa = {
 				.sin.sin_addr.s_addr = pip->daddr,
diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
index c23b99517cd5..2123344a5677 100644
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -210,7 +210,7 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
 		return;
 	}
 
-	n = neigh_lookup(arp_table(dev_net(vlandev)), &tip, vlandev);
+	n = ipv4_neigh_lookup(vlandev, &tip);
 	if (n) {
 		struct net_bridge_fdb_entry *f;
 		u8 ha[ETH_ALEN] __aligned(2);
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 95f3359d0f92..1ca4ced280ae 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -892,7 +892,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
 
 	/* Update our ARP tables */
 
-	n = __neigh_lookup(tbl, &sip, dev, 0);
+	n = ipv4_neigh_lookup(dev, &sip);
 
 	addr_type = -1;
 	if (n || arp_accept(in_dev, sip)) {
@@ -911,9 +911,14 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
 		      (addr_type == RTN_UNICAST ||
 		       (addr_type < 0 &&
 			/* postpone calculation to as late as possible */
-			inet_addr_type_dev_table(net, dev, sip) ==
-				RTN_UNICAST)))))
-			n = __neigh_lookup(tbl, &sip, dev, 1);
+			inet_addr_type_dev_table(net, dev, sip) == RTN_UNICAST))))) {
+			n = ipv4_neigh_lookup(dev, &sip);
+			if (!n) {
+				n = ipv4_neigh_create(dev, &sip);
+				if (IS_ERR(n))
+					n = NULL;
+			}
+		}
 	}
 
 	if (n) {
@@ -1102,7 +1107,6 @@ static int arp_req_set_public(struct net *net, struct arpreq *r,
 
 static int arp_req_set(struct net *net, struct arpreq *r)
 {
-	struct neigh_table *tbl = arp_table(net);
 	struct neighbour *neigh;
 	struct net_device *dev;
 	__be32 ip;
@@ -1138,7 +1142,9 @@ static int arp_req_set(struct net *net, struct arpreq *r)
 
 	ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
 
-	neigh = __neigh_lookup_errno(tbl, &ip, dev);
+	neigh = ipv4_neigh_lookup(dev, &ip);
+	if (!neigh)
+		neigh = ipv4_neigh_create(dev, &ip);
 	err = PTR_ERR(neigh);
 	if (!IS_ERR(neigh)) {
 		unsigned int state = NUD_STALE;
@@ -1174,7 +1180,6 @@ static unsigned int arp_state_to_flags(struct neighbour *neigh)
 static int arp_req_get(struct net *net, struct arpreq *r)
 {
 	__be32 ip = ((struct sockaddr_in *) &r->arp_pa)->sin_addr.s_addr;
-	struct neigh_table *tbl = arp_table(net);
 	struct neighbour *neigh;
 	struct net_device *dev;
 
@@ -1185,7 +1190,7 @@ static int arp_req_get(struct net *net, struct arpreq *r)
 	if (IS_ERR(dev))
 		return PTR_ERR(dev);
 
-	neigh = neigh_lookup(tbl, &ip, dev);
+	neigh = ipv4_neigh_lookup(dev, &ip);
 	if (!neigh)
 		return -ENXIO;
 
@@ -1210,12 +1215,13 @@ static int arp_req_get(struct net *net, struct arpreq *r)
 
 int arp_invalidate(struct net_device *dev, __be32 ip, bool force)
 {
-	struct neigh_table *tbl = arp_table(dev_net(dev));
 	struct neighbour *neigh;
 	int err = -ENXIO;
 
-	neigh = neigh_lookup(tbl, &ip, dev);
+	neigh = ipv4_neigh_lookup(dev, &ip);
 	if (neigh) {
+		struct neigh_table *tbl = neigh->tbl;
+
 		if ((READ_ONCE(neigh->nud_state) & NUD_VALID) && !force) {
 			neigh_release(neigh);
 			return 0;
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index a98c7670d2ce..235c51a6f8e5 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -610,12 +610,11 @@ static int fib_detect_death(struct fib_info *fi, int order,
 			    int dflt)
 {
 	const struct fib_nh_common *nhc = fib_info_nhc(fi, 0);
-	struct net *net = fi->fib_net;
 	int state = NUD_NONE;
 	struct neighbour *n;
 
 	if (likely(nhc->nhc_gw_family == AF_INET))
-		n = neigh_lookup(arp_table(net), &nhc->nhc_gw.ipv4, nhc->nhc_dev);
+		n = ipv4_neigh_lookup(nhc->nhc_dev, &nhc->nhc_gw.ipv4);
 	else if (IS_ENABLED(CONFIG_IPV6) && nhc->nhc_gw_family == AF_INET6)
 		n = ipv6_neigh_lookup(nhc->nhc_dev, &nhc->nhc_gw.ipv6);
 	else
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 50c842617e45..18b588dfbea7 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -817,7 +817,7 @@ static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flow
 
 	n = __ipv4_neigh_lookup(rt->dst.dev, (__force u32)new_gw);
 	if (!n)
-		n = neigh_create(arp_table(net), &new_gw, rt->dst.dev);
+		n = ipv4_neigh_create(rt->dst.dev, &new_gw);
 	if (!IS_ERR(n)) {
 		if (!(READ_ONCE(n->nud_state) & NUD_VALID)) {
 			neigh_event_send(n, NULL);
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index 19ccf1a55988..e9cb7d408ca0 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -15,6 +15,7 @@
 #include <linux/init.h>
 #include <linux/skbuff.h>
 #include <linux/moduleparam.h>
+#include <net/arp.h>
 #include <net/dst.h>
 #include <net/ndisc.h>
 #include <net/neighbour.h>
@@ -266,7 +267,9 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res,
 		} else
 #endif
 		{
-			mn = __neigh_lookup_errno(n->tbl, n->primary_key, dev);
+			mn = ipv4_neigh_lookup(dev, n->primary_key);
+			if (!mn)
+				mn = ipv4_neigh_create(dev, n->primary_key);
 		}
 		neigh_release(n);
 		if (IS_ERR(mn))
-- 
2.56.0.rc1.315.gc6ed9934b7-goog


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 net-next 8/9] neighbour: Remove __neigh_lookup() and __neigh_lookup_errno().
  2026-10-03 21:23 [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Kuniyuki Iwashima
                   ` (6 preceding siblings ...)
  2026-10-03 21:23 ` [PATCH v2 net-next 7/9] ipv4: Use ipv4_neigh_lookup() " Kuniyuki Iwashima
@ 2026-10-03 21:23 ` Kuniyuki Iwashima
  2026-10-03 21:23 ` [PATCH v2 net-next 9/9] neighbour: Check n->parms->tbl under tbl->lock in ___neigh_create() Kuniyuki Iwashima
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-03 21:23 UTC (permalink / raw)
  To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Ahern, Ido Schimmel
  Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev

Both __neigh_lookup() and __neigh_lookup_errno() not only
look up but also create a new neighbour entry.

The names were misleading and there was __neigh_lookup(..., 0),
which should have been simply written as neigh_lookup().

Now, __neigh_lookup() has only 1 user left.

Let's convert the last user to neigh_lookup() and neigh_create()
and remove __neigh_lookup() and __neigh_lookup_errno().

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 include/net/neighbour.h | 24 ------------------------
 net/core/neighbour.c    |  9 +++++++--
 2 files changed, 7 insertions(+), 26 deletions(-)

diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index ed9d9ae6d3a6..79a5a548a129 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -554,30 +554,6 @@ static inline int neigh_output(struct neighbour *n, struct sk_buff *skb,
 	return READ_ONCE(n->output)(n, skb);
 }
 
-static inline struct neighbour *
-__neigh_lookup(struct neigh_table *tbl, const void *pkey, struct net_device *dev, int creat)
-{
-	struct neighbour *n = neigh_lookup(tbl, pkey, dev);
-
-	if (n || !creat)
-		return n;
-
-	n = neigh_create(tbl, pkey, dev);
-	return IS_ERR(n) ? NULL : n;
-}
-
-static inline struct neighbour *
-__neigh_lookup_errno(struct neigh_table *tbl, const void *pkey,
-  struct net_device *dev)
-{
-	struct neighbour *n = neigh_lookup(tbl, pkey, dev);
-
-	if (n)
-		return n;
-
-	return neigh_create(tbl, pkey, dev);
-}
-
 struct neighbour_cb {
 	unsigned long sched_next;
 	unsigned int flags;
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 6ed8eb075146..0d883c043956 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -1569,8 +1569,13 @@ struct neighbour *neigh_event_ns(struct neigh_table *tbl,
 				 u8 *lladdr, void *saddr,
 				 struct net_device *dev)
 {
-	struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev,
-						 lladdr || !dev->addr_len);
+	struct neighbour *neigh = neigh_lookup(tbl, saddr, dev);
+
+	if (!neigh && (lladdr || !dev->addr_len)) {
+		neigh = neigh_create(tbl, saddr, dev);
+		if (IS_ERR(neigh))
+			neigh = NULL;
+	}
 	if (neigh)
 		neigh_update(neigh, lladdr, NUD_STALE,
 			     NEIGH_UPDATE_F_OVERRIDE, 0);
-- 
2.56.0.rc1.315.gc6ed9934b7-goog


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 net-next 9/9] neighbour: Check n->parms->tbl under tbl->lock in ___neigh_create().
  2026-10-03 21:23 [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Kuniyuki Iwashima
                   ` (7 preceding siblings ...)
  2026-10-03 21:23 ` [PATCH v2 net-next 8/9] neighbour: Remove __neigh_lookup() and __neigh_lookup_errno() Kuniyuki Iwashima
@ 2026-10-03 21:23 ` Kuniyuki Iwashima
  2026-10-06 20:39 ` [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Fernando Fernandez Mancera
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-03 21:23 UTC (permalink / raw)
  To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Ahern, Ido Schimmel
  Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev,
	netdev-bot+sashiko

Sashiko reported that if ___neigh_create() is called outside of
RCU, it could create a new entry for dev in a different netns
while __dev_change_net_namespace() is running concurrently:

  creator
    ipv6_neigh_create(dev, ...)
      tbl = nd_table(dev_net(dev))   <- old netns
      <preempted>

  __dev_change_net_namespace()
    NETDEV_UNREGISTER
    rcu_barrier()
    dev_net_set(dev, new_net)
    NETDEV_REGISTER                  <- new in6_dev

  creator resumes
    neigh_create(old_tbl, ...)
      ndisc_constructor()
        in6_dev_get(dev)             <- new in6_dev, live parms

Let's check n->parms->tbl under tbl->lock.

Fixes: cda2962b6e2b ("neighbour: Namespacify neigh_tables.")
Reported-by: netdev-bot+sashiko@kernel.org
Closes: https://lore.kernel.org/netdev/179090663305.434549.17214258093385242325@kernel.org/
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 net/core/neighbour.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 0d883c043956..90335895b415 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -690,7 +690,7 @@ ___neigh_create(struct neigh_table *tbl, const void *pkey,
 
 	hash_val = tbl->hash(n->primary_key, dev, nht->hash_rnd) >> (32 - nht->hash_shift);
 
-	if (n->parms->dead) {
+	if (n->parms->dead || n->parms->tbl != tbl) {
 		rc = ERR_PTR(-EINVAL);
 		goto out_tbl_unlock;
 	}
-- 
2.56.0.rc1.315.gc6ed9934b7-goog


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 6/9] ipv6: Use ipv6_neigh_lookup() and friends.
  2026-10-03 21:23 ` [PATCH v2 net-next 6/9] ipv6: Use ipv6_neigh_lookup() and friends Kuniyuki Iwashima
@ 2026-10-06 17:53   ` Fernando Fernandez Mancera
  2026-10-06 18:01     ` Kuniyuki Iwashima
  0 siblings, 1 reply; 18+ messages in thread
From: Fernando Fernandez Mancera @ 2026-10-06 17:53 UTC (permalink / raw)
  To: Kuniyuki Iwashima, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel
  Cc: Simon Horman, Kuniyuki Iwashima, netdev,
	syzbot+5a8857f0b4a0a7b12c62, Saeed Mahameed, Leon Romanovsky,
	Tariq Toukan, Mark Bloch, Petr Machata, Edward Cree,
	Nikolay Aleksandrov, Alexander Aring, Stefan Schmidt,
	Miquel Raynal

On 10/3/26 11:23 PM, Kuniyuki Iwashima wrote:
> syzbot reported the splat below in neigh_mark_dead() [0]
> where tbl->lock was not held while neigh_ifdown() should
> have acquired one.
> 
> This only happens when a neigh_table accidentally has a
> neighbour from a different netns.
> 
> In ip6_finish_output2(), the net argument is the caller's and
> does not always match dev_net(dev) fetched from dst. (e.g. xfrm)
> 
> It is error-prone to require callers to fetch netns and
> neigh_table and pass it with dev to neigh_lookup(), etc.
> 
> Let's replace neigh_lookup() and friends with IPv6 helpers.
> 
> Note that __neigh_lookup() is split into ipv6_neigh_lookup()
> and ipv6_neigh_create().
> 
> [0]:
> debug_locks && !(lock_is_held(&(&n->tbl->lock)->dep_map) != 0)
> WARNING: net/core/neighbour.c:154 at neigh_mark_dead+0x2fb/0x340 net/core/neighbour.c:154, CPU#0: syz.0.857/9765
> Modules linked in:
> CPU: 0 UID: 0 PID: 9765 Comm: syz.0.857 Not tainted syzkaller #0 PREEMPT(full)
> Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/24/2026
> RIP: 0010:neigh_mark_dead+0x2fb/0x340 net/core/neighbour.c:154
> Code: e8 03 42 80 3c 28 00 74 08 48 89 df e8 ee 9a 80 f8 4c 89 33 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc e8 86 95 10 f8 90 <0f> 0b 90 e9 8f fd ff ff 48 c7 c1 80 9a 7a 90 80 e1 07 80 c1 03 38
> RSP: 0018:ffffc90003726600 EFLAGS: 00010287
> RAX: ffffffff89b7467a RBX: ffff888022e86000 RCX: 0000000000080000
> RDX: ffffc900047fa000 RSI: 0000000000003e08 RDI: 0000000000003e09
> RBP: 0000000000000000 R08: 0000000000000003 R09: 0000000000000004
> R10: dffffc0000000000 R11: fffff520006e4cb8 R12: ffff888022e8613c
> R13: dffffc0000000000 R14: ffff888022e86020 R15: ffff888022e86000
> FS:  00007f9c30f046c0(0000) GS:ffff888124cc6000(0000) knlGS:0000000000000000
> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 0000000000000000 CR3: 0000000076c56000 CR4: 00000000003526f0
> Call Trace:
>   <TASK>
>   neigh_flush_one+0x27c/0x5a0 net/core/neighbour.c:388
>   neigh_flush_dev net/core/neighbour.c:432 [inline]
>   __neigh_ifdown+0x1fb/0xc70 net/core/neighbour.c:465
>   neigh_ifdown+0x1f/0x30 net/core/neighbour.c:487
>   rt6_disable_ip+0x751/0x7e0 net/ipv6/route.c:5058
>   addrconf_ifdown+0x155/0x1ad0 net/ipv6/addrconf.c:3892
>   addrconf_notify+0x1bc/0x1050 net/ipv6/addrconf.c:-1
>   notifier_call_chain+0x1a5/0x3d0 kernel/notifier.c:85
>   call_netdevice_notifiers_mtu net/core/dev.c:2350 [inline]
>   netif_set_mtu_ext+0x5ac/0x830 net/core/dev.c:9963
>   do_setlink+0xa81/0x47a0 net/core/rtnetlink.c:3247
>   rtnl_setlink+0x578/0x820 net/core/rtnetlink.c:3623
>   rtnetlink_rcv_msg+0x802/0xc00 net/core/rtnetlink.c:7159
>   netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2572
>   netlink_unicast_kernel net/netlink/af_netlink.c:1335 [inline]
>   netlink_unicast+0x7bd/0x940 net/netlink/af_netlink.c:1361
>   netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1916
>   sock_sendmsg_nosec+0x13a/0x180 net/socket.c:800
>   __sock_sendmsg net/socket.c:815 [inline]
>   sock_write_iter+0x2de/0x3e0 net/socket.c:1266
>   do_iter_readv_writev+0x612/0x8c0 fs/read_write.c:-1
>   vfs_writev+0x343/0x990 fs/read_write.c:1058
>   do_writev+0x154/0x2e0 fs/read_write.c:1104
>   do_syscall_x64 arch/x86/emlxsw_sp_ul_rif_getntry/syscall_64.c:61 [inline]
>   do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84
>   entry_SYSCALL_64_after_hwframe+0x77/0x7f
> RIP: 0033:0x7f9c2ff9e159
> Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
> RSP: 002b:00007f9c30f04028 EFLAGS: 00000246 ORIG_RAX: 0000000000000014
> RAX: ffffffffffffffda RBX: 00007f9c30225fa0 RCX: 00007f9c2ff9e159
> RDX: 0000000000000001 RSI: 00002000000003c0 RDI: 0000000000000004
> RBP: 00007f9c3003506b R08: 0000000000000000 R09: 0000000000000000
> R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
> R13: 00007f9c30226038 R14: 00007f9c30225fa0 R15: 00007ffed3c74808
>   </TASK>
> 
> Fixes: df5f4f372de5 ("ipv6: Replace &nd_tbl with nd_table(net).")
> Reported-by: syzbot+5a8857f0b4a0a7b12c62@syzkaller.appspotmail.com
> Closes: https://lore.kernel.org/netdev/6abd69a9.80e1c6cc.22483f.0005.GAE@google.com/
> Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
> ---
> v2: Convert __teql_resolve()
> 
> Cc: Saeed Mahameed <saeedm@nvidia.com>
> Cc: Leon Romanovsky <leon@kernel.org>
> Cc: Tariq Toukan <tariqt@nvidia.com>
> Cc: Mark Bloch <mbloch@nvidia.com>
> Cc: Petr Machata <petrm@nvidia.com>
> Cc: Edward Cree <ecree.xilinx@gmail.com>
> Cc: Nikolay Aleksandrov <razor@blackwall.org>
> Cc: Alexander Aring <alex.aring@gmail.com>
> Cc: Stefan Schmidt <stefan@datenfreihafen.org>
> Cc: Miquel Raynal <miquel.raynal@bootlin.com>
> ---
>   .../mellanox/mlx5/core/en/tc_tun_encap.c      | 13 +++----
>   .../ethernet/mellanox/mlxsw/spectrum_router.c | 29 ++++++++-------
>   .../ethernet/mellanox/mlxsw/spectrum_span.c   | 24 ++++++++-----
>   .../netronome/nfp/flower/tunnel_conf.c        |  4 +--
>   drivers/net/ethernet/sfc/tc_counters.c        |  5 ++-
>   drivers/net/vrf.c                             |  4 +--
>   drivers/net/vxlan/vxlan_core.c                |  6 ++--
>   include/net/ndisc.h                           |  7 ++--
>   net/bridge/br_arp_nd_proxy.c                  |  2 +-
>   net/ieee802154/6lowpan/tx.c                   |  3 +-
>   net/ipv4/fib_semantics.c                      |  2 +-
>   net/ipv6/ip6_output.c                         |  2 +-
>   net/ipv6/ndisc.c                              | 36 ++++++++++++-------
>   net/ipv6/route.c                              | 11 +++---
>   net/sched/sch_teql.c                          | 12 ++++++-
>   15 files changed, 90 insertions(+), 70 deletions(-)
> 
> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
> index 67c12ca19d59..fe0258375ef6 100644
> --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
> @@ -439,21 +439,18 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
>   	if (neigh_used) {
>   		struct net_device *dev = READ_ONCE(nhe->neigh_dev);
>   		struct net *net = dev_net(dev);
> -		struct neigh_table *tbl;
>   
>   		nhe->reported_lastuse = jiffies;
>   
> +		/* find the relevant neigh according to the cached device and
> +		 * dst ip pair
> +		 */
>   #if IS_ENABLED(CONFIG_IPV6)
>   		if (m_neigh->family != AF_INET)
> -			tbl = nd_table(net);
> +			n = ipv6_neigh_lookup(dev, &m_neigh->dst_ip);
>   		else
>   #endif
> -			tbl = arp_table(net);
> -
> -		/* find the relevant neigh according to the cached device and
> -		 * dst ip pair
> -		 */
> -		n = neigh_lookup(tbl, &m_neigh->dst_ip, dev);
> +			n = neigh_lookup(arp_table(net), &m_neigh->dst_ip, dev);
>   		if (!n)
>   			return;
>   
> diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
> index ba8a7a44ce9e..1f4753213b9c 100644
> --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
> +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
> @@ -2449,8 +2449,6 @@ static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
>   						   char *rauhtd_pl,
>   						   int rec_index)
>   {
> -	struct net *net = mlxsw_sp_net(mlxsw_sp);
> -	struct neigh_table *tbl;
>   	struct net_device *dev;
>   	struct neighbour *n;
>   	struct in6_addr dip;
> @@ -2464,9 +2462,8 @@ static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
>   		return;
>   	}
>   
> -	tbl = nd_table(net);
>   	dev = mlxsw_sp_rif_dev(mlxsw_sp->router->rifs[rif]);
> -	n = neigh_lookup(tbl, &dip, dev);
> +	n = ipv6_neigh_lookup(dev, &dip);


Hi Kuniyuki,

I have checked Sashiko's feedback [0] and I think it is right.

mlxsw_sp_router_ul_rif_get() can be called with a NULL mlxsw_sp_crif 
pointer which then would call mlxsw_sp_ul_rif_create() and there during 
the alloc a rif with a NULL crif is created making this feedback being 
correct.

I think a simple NULL check here for dev should be enough.

[0] 
https://sashiko.dev/#/message/20261003212336.1304988-7-kuniyu%40google.com

Thanks!

>   	if (!n)
>   		return;
>   
> @@ -4312,17 +4309,23 @@ mlxsw_sp_nexthop_neigh_lookup(struct mlxsw_sp_nexthop *nh)
>   	net = dev_net(dev);
>   
>   #if IS_ENABLED(CONFIG_IPV6)
> -	if (nh->family == AF_INET6)
> -		tbl = nd_table(net);
> -	else
> +	if (nh->family == AF_INET6) {
> +		n = ipv6_neigh_lookup(dev, &nh->gw_addr);
> +		if (!n) {
> +			n = ipv6_neigh_create(dev, &nh->gw_addr);
> +			if (!IS_ERR(n))
> +				neigh_event_send(n, NULL);
> +		}
> +	} else
>   #endif
> +	{
>   		tbl = arp_table(net);
> -
> -	n = neigh_lookup(tbl, &nh->gw_addr, dev);
> -	if (!n) {
> -		n = neigh_create(tbl, &nh->gw_addr, dev);
> -		if (!IS_ERR(n))
> -			neigh_event_send(n, NULL);
> +		n = neigh_lookup(tbl, &nh->gw_addr, dev);
> +		if (!n) {
> +			n = neigh_create(tbl, &nh->gw_addr, dev);
> +			if (!IS_ERR(n))
> +				neigh_event_send(n, NULL);
> +		}
>   	}
>   
>   	return n;
> diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
> index d34d2040177b..79947f68b10d 100644
> --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
> +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
> @@ -235,17 +235,23 @@ static int mlxsw_sp_span_dmac(int family,
>   	int err = 0;
>   
>   #if IS_ENABLED(CONFIG_IPV6_GRE)
> -	if (family == AF_INET6)
> -		tbl = nd_table(net);
> -	else
> +	if (family == AF_INET6) {
> +		neigh = ipv6_neigh_lookup(dev, pkey);
> +		if (!neigh) {
> +			neigh = ipv6_neigh_create(dev, pkey);
> +			if (IS_ERR(neigh))
> +				return PTR_ERR(neigh);
> +		}
> +	} else
>   #endif
> +	{
>   		tbl = arp_table(net);
> -
> -	neigh = neigh_lookup(tbl, pkey, dev);
> -	if (!neigh) {
> -		neigh = neigh_create(tbl, pkey, dev);
> -		if (IS_ERR(neigh))
> -			return PTR_ERR(neigh);
> +		neigh = neigh_lookup(tbl, pkey, dev);
> +		if (!neigh) {
> +			neigh = neigh_create(tbl, pkey, dev);
> +			if (IS_ERR(neigh))
> +				return PTR_ERR(neigh);
> +		}
>   	}
>   
>   	neigh_event_send(neigh, NULL);
> diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
> index d650d33e3709..27d80ec8e895 100644
> --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
> +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
> @@ -253,7 +253,6 @@ void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb)
>   #if IS_ENABLED(CONFIG_IPV6)
>   	struct nfp_tun_active_tuns_v6 *payload;
>   	struct net_device *netdev;
> -	struct neigh_table *tbl;
>   	int count, i, pay_len;
>   	struct neighbour *n;
>   	void *ipv6_add;
> @@ -280,8 +279,7 @@ void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb)
>   		if (!netdev)
>   			continue;
>   
> -		tbl = nd_table(dev_net(netdev));
> -		n = neigh_lookup(tbl, ipv6_add, netdev);
> +		n = ipv6_neigh_lookup(netdev, ipv6_add);
>   		if (!n)
>   			continue;
>   
> diff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c
> index 793a562fc1f7..ae6cc6b93864 100644
> --- a/drivers/net/ethernet/sfc/tc_counters.c
> +++ b/drivers/net/ethernet/sfc/tc_counters.c
> @@ -116,9 +116,8 @@ static void efx_tc_counter_work(struct work_struct *work)
>   					 encap->neigh->egdev);
>   		else
>   #if IS_ENABLED(CONFIG_IPV6)
> -			n = neigh_lookup(nd_table(net),
> -					 &encap->neigh->dst_ip6,
> -					 encap->neigh->egdev);
> +			n = ipv6_neigh_lookup(encap->neigh->egdev,
> +					      &encap->neigh->dst_ip6);
>   #else
>   			n = NULL;
>   #endif
> diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
> index 320f3584a43b..79d433f49f80 100644
> --- a/drivers/net/vrf.c
> +++ b/drivers/net/vrf.c
> @@ -614,9 +614,9 @@ static int vrf_finish_output6(struct net *net, struct sock *sk,
>   
>   	rcu_read_lock();
>   	nexthop = rt6_nexthop(dst_rt6_info(dst), &ipv6_hdr(skb)->daddr);
> -	neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
> +	neigh = __ipv6_neigh_lookup_noref(dev, nexthop);
>   	if (unlikely(!neigh))
> -		neigh = __neigh_create(nd_table(net), nexthop, dst->dev, false);
> +		neigh = ipv6_neigh_create_noref(dev, nexthop);
>   	if (!IS_ERR(neigh)) {
>   		sock_confirm_neigh(skb, neigh);
>   		ret = neigh_output(neigh, skb, false);
> diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
> index 27b0b6567d52..513779c07621 100644
> --- a/drivers/net/vxlan/vxlan_core.c
> +++ b/drivers/net/vxlan/vxlan_core.c
> @@ -2091,8 +2091,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb,
>   	    ipv6_addr_is_multicast(&msg->target))
>   		goto out;
>   
> -	n = neigh_lookup(nd_table(dev_net(dev)), &msg->target, dev);
> -
> +	n = ipv6_neigh_lookup(dev, &msg->target);
>   	if (n) {
>   		struct vxlan_rdst *rdst = NULL;
>   		u8 ha[ETH_ALEN] __aligned(2);
> @@ -2189,9 +2188,8 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb,
>   		if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
>   			return false;
>   
> -		tbl = nd_table(dev_net(dev));
>   		pip6 = ipv6_hdr(skb);
> -		n = neigh_lookup(tbl, &pip6->daddr, dev);
> +		n = ipv6_neigh_lookup(dev, &pip6->daddr);
>   		if (!n && (cfg->flags & VXLAN_F_L3MISS)) {
>   			union vxlan_addr ipa = {
>   				.sin6.sin6_addr = pip6->daddr,
> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> index 2fce6fccf55a..91898a2475e7 100644
> --- a/include/net/ndisc.h
> +++ b/include/net/ndisc.h
> @@ -423,11 +423,8 @@ static inline struct neighbour *ip_neigh_gw6(struct net_device *dev,
>   	struct neighbour *neigh;
>   
>   	neigh = __ipv6_neigh_lookup_noref(dev, addr);
> -	if (unlikely(!neigh)) {
> -		struct neigh_table *tbl = nd_table(dev_net(dev));
> -
> -		neigh = __neigh_create(tbl, addr, dev, false);
> -	}
> +	if (unlikely(!neigh))
> +		neigh = ipv6_neigh_create_noref(dev, addr);
>   
>   	return neigh;
>   #else
> diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
> index ba4a63840b21..c23b99517cd5 100644
> --- a/net/bridge/br_arp_nd_proxy.c
> +++ b/net/bridge/br_arp_nd_proxy.c
> @@ -487,7 +487,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
>   		return;
>   	}
>   
> -	n = neigh_lookup(nd_table(dev_net(vlandev)), &msg->target, vlandev);
> +	n = ipv6_neigh_lookup(vlandev, &msg->target);
>   	if (n) {
>   		struct net_bridge_fdb_entry *f;
>   		u8 ha[ETH_ALEN] __aligned(2);
> diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c
> index 4f511476b992..b261daedc290 100644
> --- a/net/ieee802154/6lowpan/tx.c
> +++ b/net/ieee802154/6lowpan/tx.c
> @@ -58,9 +58,8 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
>   		info->daddr.mode = IEEE802154_ADDR_SHORT;
>   	} else {
>   		__le16 short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
> -		struct neigh_table *tbl = nd_table(dev_net(ldev));
>   
> -		n = neigh_lookup(tbl, &hdr->daddr, ldev);
> +		n = ipv6_neigh_lookup(ldev, &hdr->daddr);
>   		if (n) {
>   			llneigh = lowpan_802154_neigh(neighbour_priv(n));
>   			read_lock_bh(&n->lock);
> diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
> index e3bcc25229b0..a98c7670d2ce 100644
> --- a/net/ipv4/fib_semantics.c
> +++ b/net/ipv4/fib_semantics.c
> @@ -617,7 +617,7 @@ static int fib_detect_death(struct fib_info *fi, int order,
>   	if (likely(nhc->nhc_gw_family == AF_INET))
>   		n = neigh_lookup(arp_table(net), &nhc->nhc_gw.ipv4, nhc->nhc_dev);
>   	else if (IS_ENABLED(CONFIG_IPV6) && nhc->nhc_gw_family == AF_INET6)
> -		n = neigh_lookup(nd_table(net), &nhc->nhc_gw.ipv6, nhc->nhc_dev);
> +		n = ipv6_neigh_lookup(nhc->nhc_dev, &nhc->nhc_gw.ipv6);
>   	else
>   		n = NULL;
>   
> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 10146a57b69e..25fe0ba98b1a 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -127,7 +127,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
>   
>   	if (IS_ERR_OR_NULL(neigh)) {
>   		if (unlikely(!neigh))
> -			neigh = __neigh_create(nd_table(net), nexthop, dev, false);
> +			neigh = ipv6_neigh_create_noref(dev, nexthop);
>   		if (IS_ERR(neigh)) {
>   			IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTNOROUTES);
>   			kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_CREATEFAIL);
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index e1cbffaa0ad0..0ed1a619372b 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -947,8 +947,12 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
>   	 *	update / create cache entry
>   	 *	for the source address
>   	 */
> -	neigh = __neigh_lookup(tbl, saddr, dev,
> -			       !inc || lladdr || !dev->addr_len);
> +	neigh = ipv6_neigh_lookup(dev, saddr);
> +	if (!neigh && (!inc || lladdr || !dev->addr_len)) {
> +		neigh = ipv6_neigh_create(dev, saddr);
> +		if (IS_ERR(neigh))
> +			neigh = NULL;
> +	}
>   	if (neigh)
>   		ndisc_update(dev, neigh, lladdr, NUD_STALE,
>   			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
> @@ -998,7 +1002,6 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
>   	struct net *net = dev_net(dev);
>   	struct ndisc_options ndopts;
>   	struct inet6_ifaddr *ifp;
> -	struct neigh_table *tbl;
>   	struct neighbour *neigh;
>   	struct inet6_dev *idev;
>   	u8 *lladdr = NULL;
> @@ -1063,8 +1066,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
>   		return reason;
>   	}
>   
> -	tbl = nd_table(net);
> -	neigh = neigh_lookup(tbl, &msg->target, dev);
> +	neigh = ipv6_neigh_lookup(dev, &msg->target);
>   
>   	/* RFC 9131 updates original Neighbour Discovery RFC 4861.
>   	 * NAs with Target LL Address option can now create a STALE neighbor
> @@ -1093,7 +1095,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
>   			return reason;
>   		}
>   		if (!neigh)
> -			neigh = neigh_create(tbl, &msg->target, dev);
> +			neigh = ipv6_neigh_create(dev, &msg->target);
>   		new_state = NUD_STALE;
>   	}
>   
> @@ -1108,7 +1110,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(tbl, &msg->target, dev)) {
> +		    pneigh_lookup(nd_table(dev_net(dev)), &msg->target, dev)) {
>   			/* XXX: idev->cnf.proxy_ndp */
>   			goto out;
>   		}
> @@ -1141,7 +1143,6 @@ static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
>   	unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
>   	struct net_device *dev = skb->dev;
>   	struct ndisc_options ndopts;
> -	struct neigh_table *tbl;
>   	struct neighbour *neigh;
>   	struct inet6_dev *idev;
>   	u8 *lladdr = NULL;
> @@ -1177,8 +1178,12 @@ static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
>   			goto out;
>   	}
>   
> -	tbl = nd_table(dev_net(dev));
> -	neigh = __neigh_lookup(tbl, saddr, dev, 1);
> +	neigh = ipv6_neigh_lookup(dev, saddr);
> +	if (!neigh) {
> +		neigh = ipv6_neigh_create(dev, saddr);
> +		if (IS_ERR(neigh))
> +			goto out;
> +	}
>   	if (neigh) {
>   		ndisc_update(dev, neigh, lladdr, NUD_STALE,
>   			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
> @@ -1478,9 +1483,14 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
>   	 *	Process options.
>   	 */
>   
> -	if (!neigh)
> -		neigh = __neigh_lookup(nd_table(net), &ipv6_hdr(skb)->saddr,
> -				       skb->dev, 1);
> +	if (!neigh) {
> +		neigh = ipv6_neigh_lookup(skb->dev, &ipv6_hdr(skb)->saddr);
> +		if (!neigh) {
> +			neigh = ipv6_neigh_create(skb->dev, &ipv6_hdr(skb)->saddr);
> +			if (IS_ERR(neigh))
> +				neigh = NULL;
> +		}
> +	}
>   	if (neigh) {
>   		u8 *lladdr = NULL;
>   		if (ndopts.nd_opts_src_lladdr) {
> diff --git a/net/ipv6/route.c b/net/ipv6/route.c
> index 8baac4d85251..ea9f0a4c34f9 100644
> --- a/net/ipv6/route.c
> +++ b/net/ipv6/route.c
> @@ -218,7 +218,7 @@ struct neighbour *__ip6_dst_neigh_lookup(const struct in6_addr *gw,
>   	if (n)
>   		return n;
>   
> -	n = neigh_create(nd_table(dev_net(dev)), daddr, dev);
> +	n = ipv6_neigh_create(dev, daddr);
>   	return IS_ERR(n) ? NULL : n;
>   }
>   
> @@ -4277,9 +4277,12 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
>   	 */
>   	dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
>   
> -	neigh = __neigh_lookup(nd_table(dev_net(dev)), &msg->target, dev, 1);
> -	if (!neigh)
> -		return;
> +	neigh = ipv6_neigh_lookup(dev, &msg->target);
> +	if (!neigh) {
> +		neigh = ipv6_neigh_create(dev, &msg->target);
> +		if (IS_ERR(neigh))
> +			return;
> +	}
>   
>   	/*
>   	 *	We have finally decided to accept it.
> diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
> index 409ce50cc0db..19ccf1a55988 100644
> --- a/net/sched/sch_teql.c
> +++ b/net/sched/sch_teql.c
> @@ -16,6 +16,7 @@
>   #include <linux/skbuff.h>
>   #include <linux/moduleparam.h>
>   #include <net/dst.h>
> +#include <net/ndisc.h>
>   #include <net/neighbour.h>
>   #include <net/pkt_sched.h>
>   
> @@ -257,7 +258,16 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res,
>   	if (dst->dev != dev) {
>   		struct neighbour *mn;
>   
> -		mn = __neigh_lookup_errno(n->tbl, n->primary_key, dev);
> +#if IS_ENABLED(CONFIG_IPV6)
> +		if (n->tbl->family == AF_INET6) {
> +			mn = ipv6_neigh_lookup(dev, n->primary_key);
> +			if (!mn)
> +				mn = ipv6_neigh_create(dev, n->primary_key);
> +		} else
> +#endif
> +		{
> +			mn = __neigh_lookup_errno(n->tbl, n->primary_key, dev);
> +		}
>   		neigh_release(n);
>   		if (IS_ERR(mn))
>   			return PTR_ERR(mn);


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 6/9] ipv6: Use ipv6_neigh_lookup() and friends.
  2026-10-06 17:53   ` Fernando Fernandez Mancera
@ 2026-10-06 18:01     ` Kuniyuki Iwashima
  2026-10-06 20:34       ` Fernando Fernandez Mancera
  0 siblings, 1 reply; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-06 18:01 UTC (permalink / raw)
  To: Fernando Fernandez Mancera
  Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Ahern, Ido Schimmel, Simon Horman,
	Kuniyuki Iwashima, netdev, syzbot+5a8857f0b4a0a7b12c62,
	Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Mark Bloch,
	Petr Machata, Edward Cree, Nikolay Aleksandrov, Alexander Aring,
	Stefan Schmidt, Miquel Raynal

On Tue, Oct 6, 2026 at 10:53 AM Fernando Fernandez Mancera
<fmancera@suse.de> wrote:
>
> On 10/3/26 11:23 PM, Kuniyuki Iwashima wrote:
> > syzbot reported the splat below in neigh_mark_dead() [0]
> > where tbl->lock was not held while neigh_ifdown() should
> > have acquired one.
> >
> > This only happens when a neigh_table accidentally has a
> > neighbour from a different netns.
> >
> > In ip6_finish_output2(), the net argument is the caller's and
> > does not always match dev_net(dev) fetched from dst. (e.g. xfrm)
> >
> > It is error-prone to require callers to fetch netns and
> > neigh_table and pass it with dev to neigh_lookup(), etc.
> >
> > Let's replace neigh_lookup() and friends with IPv6 helpers.
> >
> > Note that __neigh_lookup() is split into ipv6_neigh_lookup()
> > and ipv6_neigh_create().
> >
> > [0]:
> > debug_locks && !(lock_is_held(&(&n->tbl->lock)->dep_map) != 0)
> > WARNING: net/core/neighbour.c:154 at neigh_mark_dead+0x2fb/0x340 net/core/neighbour.c:154, CPU#0: syz.0.857/9765
> > Modules linked in:
> > CPU: 0 UID: 0 PID: 9765 Comm: syz.0.857 Not tainted syzkaller #0 PREEMPT(full)
> > Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/24/2026
> > RIP: 0010:neigh_mark_dead+0x2fb/0x340 net/core/neighbour.c:154
> > Code: e8 03 42 80 3c 28 00 74 08 48 89 df e8 ee 9a 80 f8 4c 89 33 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc e8 86 95 10 f8 90 <0f> 0b 90 e9 8f fd ff ff 48 c7 c1 80 9a 7a 90 80 e1 07 80 c1 03 38
> > RSP: 0018:ffffc90003726600 EFLAGS: 00010287
> > RAX: ffffffff89b7467a RBX: ffff888022e86000 RCX: 0000000000080000
> > RDX: ffffc900047fa000 RSI: 0000000000003e08 RDI: 0000000000003e09
> > RBP: 0000000000000000 R08: 0000000000000003 R09: 0000000000000004
> > R10: dffffc0000000000 R11: fffff520006e4cb8 R12: ffff888022e8613c
> > R13: dffffc0000000000 R14: ffff888022e86020 R15: ffff888022e86000
> > FS:  00007f9c30f046c0(0000) GS:ffff888124cc6000(0000) knlGS:0000000000000000
> > CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> > CR2: 0000000000000000 CR3: 0000000076c56000 CR4: 00000000003526f0
> > Call Trace:
> >   <TASK>
> >   neigh_flush_one+0x27c/0x5a0 net/core/neighbour.c:388
> >   neigh_flush_dev net/core/neighbour.c:432 [inline]
> >   __neigh_ifdown+0x1fb/0xc70 net/core/neighbour.c:465
> >   neigh_ifdown+0x1f/0x30 net/core/neighbour.c:487
> >   rt6_disable_ip+0x751/0x7e0 net/ipv6/route.c:5058
> >   addrconf_ifdown+0x155/0x1ad0 net/ipv6/addrconf.c:3892
> >   addrconf_notify+0x1bc/0x1050 net/ipv6/addrconf.c:-1
> >   notifier_call_chain+0x1a5/0x3d0 kernel/notifier.c:85
> >   call_netdevice_notifiers_mtu net/core/dev.c:2350 [inline]
> >   netif_set_mtu_ext+0x5ac/0x830 net/core/dev.c:9963
> >   do_setlink+0xa81/0x47a0 net/core/rtnetlink.c:3247
> >   rtnl_setlink+0x578/0x820 net/core/rtnetlink.c:3623
> >   rtnetlink_rcv_msg+0x802/0xc00 net/core/rtnetlink.c:7159
> >   netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2572
> >   netlink_unicast_kernel net/netlink/af_netlink.c:1335 [inline]
> >   netlink_unicast+0x7bd/0x940 net/netlink/af_netlink.c:1361
> >   netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1916
> >   sock_sendmsg_nosec+0x13a/0x180 net/socket.c:800
> >   __sock_sendmsg net/socket.c:815 [inline]
> >   sock_write_iter+0x2de/0x3e0 net/socket.c:1266
> >   do_iter_readv_writev+0x612/0x8c0 fs/read_write.c:-1
> >   vfs_writev+0x343/0x990 fs/read_write.c:1058
> >   do_writev+0x154/0x2e0 fs/read_write.c:1104
> >   do_syscall_x64 arch/x86/emlxsw_sp_ul_rif_getntry/syscall_64.c:61 [inline]
> >   do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84
> >   entry_SYSCALL_64_after_hwframe+0x77/0x7f
> > RIP: 0033:0x7f9c2ff9e159
> > Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
> > RSP: 002b:00007f9c30f04028 EFLAGS: 00000246 ORIG_RAX: 0000000000000014
> > RAX: ffffffffffffffda RBX: 00007f9c30225fa0 RCX: 00007f9c2ff9e159
> > RDX: 0000000000000001 RSI: 00002000000003c0 RDI: 0000000000000004
> > RBP: 00007f9c3003506b R08: 0000000000000000 R09: 0000000000000000
> > R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
> > R13: 00007f9c30226038 R14: 00007f9c30225fa0 R15: 00007ffed3c74808
> >   </TASK>
> >
> > Fixes: df5f4f372de5 ("ipv6: Replace &nd_tbl with nd_table(net).")
> > Reported-by: syzbot+5a8857f0b4a0a7b12c62@syzkaller.appspotmail.com
> > Closes: https://lore.kernel.org/netdev/6abd69a9.80e1c6cc.22483f.0005.GAE@google.com/
> > Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
> > ---
> > v2: Convert __teql_resolve()
> >
> > Cc: Saeed Mahameed <saeedm@nvidia.com>
> > Cc: Leon Romanovsky <leon@kernel.org>
> > Cc: Tariq Toukan <tariqt@nvidia.com>
> > Cc: Mark Bloch <mbloch@nvidia.com>
> > Cc: Petr Machata <petrm@nvidia.com>
> > Cc: Edward Cree <ecree.xilinx@gmail.com>
> > Cc: Nikolay Aleksandrov <razor@blackwall.org>
> > Cc: Alexander Aring <alex.aring@gmail.com>
> > Cc: Stefan Schmidt <stefan@datenfreihafen.org>
> > Cc: Miquel Raynal <miquel.raynal@bootlin.com>
> > ---
> >   .../mellanox/mlx5/core/en/tc_tun_encap.c      | 13 +++----
> >   .../ethernet/mellanox/mlxsw/spectrum_router.c | 29 ++++++++-------
> >   .../ethernet/mellanox/mlxsw/spectrum_span.c   | 24 ++++++++-----
> >   .../netronome/nfp/flower/tunnel_conf.c        |  4 +--
> >   drivers/net/ethernet/sfc/tc_counters.c        |  5 ++-
> >   drivers/net/vrf.c                             |  4 +--
> >   drivers/net/vxlan/vxlan_core.c                |  6 ++--
> >   include/net/ndisc.h                           |  7 ++--
> >   net/bridge/br_arp_nd_proxy.c                  |  2 +-
> >   net/ieee802154/6lowpan/tx.c                   |  3 +-
> >   net/ipv4/fib_semantics.c                      |  2 +-
> >   net/ipv6/ip6_output.c                         |  2 +-
> >   net/ipv6/ndisc.c                              | 36 ++++++++++++-------
> >   net/ipv6/route.c                              | 11 +++---
> >   net/sched/sch_teql.c                          | 12 ++++++-
> >   15 files changed, 90 insertions(+), 70 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
> > index 67c12ca19d59..fe0258375ef6 100644
> > --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
> > +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
> > @@ -439,21 +439,18 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
> >       if (neigh_used) {
> >               struct net_device *dev = READ_ONCE(nhe->neigh_dev);
> >               struct net *net = dev_net(dev);
> > -             struct neigh_table *tbl;
> >
> >               nhe->reported_lastuse = jiffies;
> >
> > +             /* find the relevant neigh according to the cached device and
> > +              * dst ip pair
> > +              */
> >   #if IS_ENABLED(CONFIG_IPV6)
> >               if (m_neigh->family != AF_INET)
> > -                     tbl = nd_table(net);
> > +                     n = ipv6_neigh_lookup(dev, &m_neigh->dst_ip);
> >               else
> >   #endif
> > -                     tbl = arp_table(net);
> > -
> > -             /* find the relevant neigh according to the cached device and
> > -              * dst ip pair
> > -              */
> > -             n = neigh_lookup(tbl, &m_neigh->dst_ip, dev);
> > +                     n = neigh_lookup(arp_table(net), &m_neigh->dst_ip, dev);
> >               if (!n)
> >                       return;
> >
> > diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
> > index ba8a7a44ce9e..1f4753213b9c 100644
> > --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
> > +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
> > @@ -2449,8 +2449,6 @@ static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
> >                                                  char *rauhtd_pl,
> >                                                  int rec_index)
> >   {
> > -     struct net *net = mlxsw_sp_net(mlxsw_sp);
> > -     struct neigh_table *tbl;
> >       struct net_device *dev;
> >       struct neighbour *n;
> >       struct in6_addr dip;
> > @@ -2464,9 +2462,8 @@ static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
> >               return;
> >       }
> >
> > -     tbl = nd_table(net);
> >       dev = mlxsw_sp_rif_dev(mlxsw_sp->router->rifs[rif]);
> > -     n = neigh_lookup(tbl, &dip, dev);
> > +     n = ipv6_neigh_lookup(dev, &dip);
>
>
> Hi Kuniyuki,
>
> I have checked Sashiko's feedback [0] and I think it is right.

I think this is false-positive.

This was pointed out in v1 too, but I did not add NULL check to avoid
defensive programming.

Hardware only reports neighbour activity via RAUHTD for entries programmed
into RAUHT by mlxsw_sp_neigh_entry_update().  Those entries are created only
in mlxsw_sp_neigh_entry_create(), which resolves the RIF via
mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev) where n->dev is always non-NULL.
Thus, underlay and loopback RIFs (where mlxsw_sp_rif_dev() is NULL) never
have neighbour entries programmed in hardware.

Also, when a RIF is destroyed in mlxsw_sp_rif_destroy(), all of its
RAUHT entries are synchronously removed from hardware in
mlxsw_sp_neigh_rif_gone_sync() and router->rifs[rif] is cleared under
router->lock (the same lock held during the RAUHTD dump) before the RIF
index can be reused.


>
> mlxsw_sp_router_ul_rif_get() can be called with a NULL mlxsw_sp_crif
> pointer which then would call mlxsw_sp_ul_rif_create() and there during
> the alloc a rif with a NULL crif is created making this feedback being
> correct.
>
> I think a simple NULL check here for dev should be enough.
>
> [0]
> https://sashiko.dev/#/message/20261003212336.1304988-7-kuniyu%40google.com

NIPA's Sashiko is pretty better than Gemini 3.1 these days.
I hope our instance support Gemini 4 soon :)
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261003212336.1304988-1-kuniyu%40google.com


>
> Thanks!
>
> >       if (!n)
> >               return;
> >
> > @@ -4312,17 +4309,23 @@ mlxsw_sp_nexthop_neigh_lookup(struct mlxsw_sp_nexthop *nh)
> >       net = dev_net(dev);
> >
> >   #if IS_ENABLED(CONFIG_IPV6)
> > -     if (nh->family == AF_INET6)
> > -             tbl = nd_table(net);
> > -     else
> > +     if (nh->family == AF_INET6) {
> > +             n = ipv6_neigh_lookup(dev, &nh->gw_addr);
> > +             if (!n) {
> > +                     n = ipv6_neigh_create(dev, &nh->gw_addr);
> > +                     if (!IS_ERR(n))
> > +                             neigh_event_send(n, NULL);
> > +             }
> > +     } else
> >   #endif
> > +     {
> >               tbl = arp_table(net);
> > -
> > -     n = neigh_lookup(tbl, &nh->gw_addr, dev);
> > -     if (!n) {
> > -             n = neigh_create(tbl, &nh->gw_addr, dev);
> > -             if (!IS_ERR(n))
> > -                     neigh_event_send(n, NULL);
> > +             n = neigh_lookup(tbl, &nh->gw_addr, dev);
> > +             if (!n) {
> > +                     n = neigh_create(tbl, &nh->gw_addr, dev);
> > +                     if (!IS_ERR(n))
> > +                             neigh_event_send(n, NULL);
> > +             }
> >       }
> >
> >       return n;
> > diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
> > index d34d2040177b..79947f68b10d 100644
> > --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
> > +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
> > @@ -235,17 +235,23 @@ static int mlxsw_sp_span_dmac(int family,
> >       int err = 0;
> >
> >   #if IS_ENABLED(CONFIG_IPV6_GRE)
> > -     if (family == AF_INET6)
> > -             tbl = nd_table(net);
> > -     else
> > +     if (family == AF_INET6) {
> > +             neigh = ipv6_neigh_lookup(dev, pkey);
> > +             if (!neigh) {
> > +                     neigh = ipv6_neigh_create(dev, pkey);
> > +                     if (IS_ERR(neigh))
> > +                             return PTR_ERR(neigh);
> > +             }
> > +     } else
> >   #endif
> > +     {
> >               tbl = arp_table(net);
> > -
> > -     neigh = neigh_lookup(tbl, pkey, dev);
> > -     if (!neigh) {
> > -             neigh = neigh_create(tbl, pkey, dev);
> > -             if (IS_ERR(neigh))
> > -                     return PTR_ERR(neigh);
> > +             neigh = neigh_lookup(tbl, pkey, dev);
> > +             if (!neigh) {
> > +                     neigh = neigh_create(tbl, pkey, dev);
> > +                     if (IS_ERR(neigh))
> > +                             return PTR_ERR(neigh);
> > +             }
> >       }
> >
> >       neigh_event_send(neigh, NULL);
> > diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
> > index d650d33e3709..27d80ec8e895 100644
> > --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
> > +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
> > @@ -253,7 +253,6 @@ void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb)
> >   #if IS_ENABLED(CONFIG_IPV6)
> >       struct nfp_tun_active_tuns_v6 *payload;
> >       struct net_device *netdev;
> > -     struct neigh_table *tbl;
> >       int count, i, pay_len;
> >       struct neighbour *n;
> >       void *ipv6_add;
> > @@ -280,8 +279,7 @@ void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb)
> >               if (!netdev)
> >                       continue;
> >
> > -             tbl = nd_table(dev_net(netdev));
> > -             n = neigh_lookup(tbl, ipv6_add, netdev);
> > +             n = ipv6_neigh_lookup(netdev, ipv6_add);
> >               if (!n)
> >                       continue;
> >
> > diff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c
> > index 793a562fc1f7..ae6cc6b93864 100644
> > --- a/drivers/net/ethernet/sfc/tc_counters.c
> > +++ b/drivers/net/ethernet/sfc/tc_counters.c
> > @@ -116,9 +116,8 @@ static void efx_tc_counter_work(struct work_struct *work)
> >                                        encap->neigh->egdev);
> >               else
> >   #if IS_ENABLED(CONFIG_IPV6)
> > -                     n = neigh_lookup(nd_table(net),
> > -                                      &encap->neigh->dst_ip6,
> > -                                      encap->neigh->egdev);
> > +                     n = ipv6_neigh_lookup(encap->neigh->egdev,
> > +                                           &encap->neigh->dst_ip6);
> >   #else
> >                       n = NULL;
> >   #endif
> > diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
> > index 320f3584a43b..79d433f49f80 100644
> > --- a/drivers/net/vrf.c
> > +++ b/drivers/net/vrf.c
> > @@ -614,9 +614,9 @@ static int vrf_finish_output6(struct net *net, struct sock *sk,
> >
> >       rcu_read_lock();
> >       nexthop = rt6_nexthop(dst_rt6_info(dst), &ipv6_hdr(skb)->daddr);
> > -     neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
> > +     neigh = __ipv6_neigh_lookup_noref(dev, nexthop);
> >       if (unlikely(!neigh))
> > -             neigh = __neigh_create(nd_table(net), nexthop, dst->dev, false);
> > +             neigh = ipv6_neigh_create_noref(dev, nexthop);
> >       if (!IS_ERR(neigh)) {
> >               sock_confirm_neigh(skb, neigh);
> >               ret = neigh_output(neigh, skb, false);
> > diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
> > index 27b0b6567d52..513779c07621 100644
> > --- a/drivers/net/vxlan/vxlan_core.c
> > +++ b/drivers/net/vxlan/vxlan_core.c
> > @@ -2091,8 +2091,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb,
> >           ipv6_addr_is_multicast(&msg->target))
> >               goto out;
> >
> > -     n = neigh_lookup(nd_table(dev_net(dev)), &msg->target, dev);
> > -
> > +     n = ipv6_neigh_lookup(dev, &msg->target);
> >       if (n) {
> >               struct vxlan_rdst *rdst = NULL;
> >               u8 ha[ETH_ALEN] __aligned(2);
> > @@ -2189,9 +2188,8 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb,
> >               if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
> >                       return false;
> >
> > -             tbl = nd_table(dev_net(dev));
> >               pip6 = ipv6_hdr(skb);
> > -             n = neigh_lookup(tbl, &pip6->daddr, dev);
> > +             n = ipv6_neigh_lookup(dev, &pip6->daddr);
> >               if (!n && (cfg->flags & VXLAN_F_L3MISS)) {
> >                       union vxlan_addr ipa = {
> >                               .sin6.sin6_addr = pip6->daddr,
> > diff --git a/include/net/ndisc.h b/include/net/ndisc.h
> > index 2fce6fccf55a..91898a2475e7 100644
> > --- a/include/net/ndisc.h
> > +++ b/include/net/ndisc.h
> > @@ -423,11 +423,8 @@ static inline struct neighbour *ip_neigh_gw6(struct net_device *dev,
> >       struct neighbour *neigh;
> >
> >       neigh = __ipv6_neigh_lookup_noref(dev, addr);
> > -     if (unlikely(!neigh)) {
> > -             struct neigh_table *tbl = nd_table(dev_net(dev));
> > -
> > -             neigh = __neigh_create(tbl, addr, dev, false);
> > -     }
> > +     if (unlikely(!neigh))
> > +             neigh = ipv6_neigh_create_noref(dev, addr);
> >
> >       return neigh;
> >   #else
> > diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
> > index ba4a63840b21..c23b99517cd5 100644
> > --- a/net/bridge/br_arp_nd_proxy.c
> > +++ b/net/bridge/br_arp_nd_proxy.c
> > @@ -487,7 +487,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
> >               return;
> >       }
> >
> > -     n = neigh_lookup(nd_table(dev_net(vlandev)), &msg->target, vlandev);
> > +     n = ipv6_neigh_lookup(vlandev, &msg->target);
> >       if (n) {
> >               struct net_bridge_fdb_entry *f;
> >               u8 ha[ETH_ALEN] __aligned(2);
> > diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c
> > index 4f511476b992..b261daedc290 100644
> > --- a/net/ieee802154/6lowpan/tx.c
> > +++ b/net/ieee802154/6lowpan/tx.c
> > @@ -58,9 +58,8 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
> >               info->daddr.mode = IEEE802154_ADDR_SHORT;
> >       } else {
> >               __le16 short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
> > -             struct neigh_table *tbl = nd_table(dev_net(ldev));
> >
> > -             n = neigh_lookup(tbl, &hdr->daddr, ldev);
> > +             n = ipv6_neigh_lookup(ldev, &hdr->daddr);
> >               if (n) {
> >                       llneigh = lowpan_802154_neigh(neighbour_priv(n));
> >                       read_lock_bh(&n->lock);
> > diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
> > index e3bcc25229b0..a98c7670d2ce 100644
> > --- a/net/ipv4/fib_semantics.c
> > +++ b/net/ipv4/fib_semantics.c
> > @@ -617,7 +617,7 @@ static int fib_detect_death(struct fib_info *fi, int order,
> >       if (likely(nhc->nhc_gw_family == AF_INET))
> >               n = neigh_lookup(arp_table(net), &nhc->nhc_gw.ipv4, nhc->nhc_dev);
> >       else if (IS_ENABLED(CONFIG_IPV6) && nhc->nhc_gw_family == AF_INET6)
> > -             n = neigh_lookup(nd_table(net), &nhc->nhc_gw.ipv6, nhc->nhc_dev);
> > +             n = ipv6_neigh_lookup(nhc->nhc_dev, &nhc->nhc_gw.ipv6);
> >       else
> >               n = NULL;
> >
> > diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> > index 10146a57b69e..25fe0ba98b1a 100644
> > --- a/net/ipv6/ip6_output.c
> > +++ b/net/ipv6/ip6_output.c
> > @@ -127,7 +127,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
> >
> >       if (IS_ERR_OR_NULL(neigh)) {
> >               if (unlikely(!neigh))
> > -                     neigh = __neigh_create(nd_table(net), nexthop, dev, false);
> > +                     neigh = ipv6_neigh_create_noref(dev, nexthop);
> >               if (IS_ERR(neigh)) {
> >                       IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTNOROUTES);
> >                       kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_CREATEFAIL);
> > diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> > index e1cbffaa0ad0..0ed1a619372b 100644
> > --- a/net/ipv6/ndisc.c
> > +++ b/net/ipv6/ndisc.c
> > @@ -947,8 +947,12 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
> >        *      update / create cache entry
> >        *      for the source address
> >        */
> > -     neigh = __neigh_lookup(tbl, saddr, dev,
> > -                            !inc || lladdr || !dev->addr_len);
> > +     neigh = ipv6_neigh_lookup(dev, saddr);
> > +     if (!neigh && (!inc || lladdr || !dev->addr_len)) {
> > +             neigh = ipv6_neigh_create(dev, saddr);
> > +             if (IS_ERR(neigh))
> > +                     neigh = NULL;
> > +     }
> >       if (neigh)
> >               ndisc_update(dev, neigh, lladdr, NUD_STALE,
> >                            NEIGH_UPDATE_F_WEAK_OVERRIDE|
> > @@ -998,7 +1002,6 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
> >       struct net *net = dev_net(dev);
> >       struct ndisc_options ndopts;
> >       struct inet6_ifaddr *ifp;
> > -     struct neigh_table *tbl;
> >       struct neighbour *neigh;
> >       struct inet6_dev *idev;
> >       u8 *lladdr = NULL;
> > @@ -1063,8 +1066,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
> >               return reason;
> >       }
> >
> > -     tbl = nd_table(net);
> > -     neigh = neigh_lookup(tbl, &msg->target, dev);
> > +     neigh = ipv6_neigh_lookup(dev, &msg->target);
> >
> >       /* RFC 9131 updates original Neighbour Discovery RFC 4861.
> >        * NAs with Target LL Address option can now create a STALE neighbor
> > @@ -1093,7 +1095,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
> >                       return reason;
> >               }
> >               if (!neigh)
> > -                     neigh = neigh_create(tbl, &msg->target, dev);
> > +                     neigh = ipv6_neigh_create(dev, &msg->target);
> >               new_state = NUD_STALE;
> >       }
> >
> > @@ -1108,7 +1110,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(tbl, &msg->target, dev)) {
> > +                 pneigh_lookup(nd_table(dev_net(dev)), &msg->target, dev)) {
> >                       /* XXX: idev->cnf.proxy_ndp */
> >                       goto out;
> >               }
> > @@ -1141,7 +1143,6 @@ static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
> >       unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
> >       struct net_device *dev = skb->dev;
> >       struct ndisc_options ndopts;
> > -     struct neigh_table *tbl;
> >       struct neighbour *neigh;
> >       struct inet6_dev *idev;
> >       u8 *lladdr = NULL;
> > @@ -1177,8 +1178,12 @@ static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
> >                       goto out;
> >       }
> >
> > -     tbl = nd_table(dev_net(dev));
> > -     neigh = __neigh_lookup(tbl, saddr, dev, 1);
> > +     neigh = ipv6_neigh_lookup(dev, saddr);
> > +     if (!neigh) {
> > +             neigh = ipv6_neigh_create(dev, saddr);
> > +             if (IS_ERR(neigh))
> > +                     goto out;
> > +     }
> >       if (neigh) {
> >               ndisc_update(dev, neigh, lladdr, NUD_STALE,
> >                            NEIGH_UPDATE_F_WEAK_OVERRIDE|
> > @@ -1478,9 +1483,14 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
> >        *      Process options.
> >        */
> >
> > -     if (!neigh)
> > -             neigh = __neigh_lookup(nd_table(net), &ipv6_hdr(skb)->saddr,
> > -                                    skb->dev, 1);
> > +     if (!neigh) {
> > +             neigh = ipv6_neigh_lookup(skb->dev, &ipv6_hdr(skb)->saddr);
> > +             if (!neigh) {
> > +                     neigh = ipv6_neigh_create(skb->dev, &ipv6_hdr(skb)->saddr);
> > +                     if (IS_ERR(neigh))
> > +                             neigh = NULL;
> > +             }
> > +     }
> >       if (neigh) {
> >               u8 *lladdr = NULL;
> >               if (ndopts.nd_opts_src_lladdr) {
> > diff --git a/net/ipv6/route.c b/net/ipv6/route.c
> > index 8baac4d85251..ea9f0a4c34f9 100644
> > --- a/net/ipv6/route.c
> > +++ b/net/ipv6/route.c
> > @@ -218,7 +218,7 @@ struct neighbour *__ip6_dst_neigh_lookup(const struct in6_addr *gw,
> >       if (n)
> >               return n;
> >
> > -     n = neigh_create(nd_table(dev_net(dev)), daddr, dev);
> > +     n = ipv6_neigh_create(dev, daddr);
> >       return IS_ERR(n) ? NULL : n;
> >   }
> >
> > @@ -4277,9 +4277,12 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
> >        */
> >       dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
> >
> > -     neigh = __neigh_lookup(nd_table(dev_net(dev)), &msg->target, dev, 1);
> > -     if (!neigh)
> > -             return;
> > +     neigh = ipv6_neigh_lookup(dev, &msg->target);
> > +     if (!neigh) {
> > +             neigh = ipv6_neigh_create(dev, &msg->target);
> > +             if (IS_ERR(neigh))
> > +                     return;
> > +     }
> >
> >       /*
> >        *      We have finally decided to accept it.
> > diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
> > index 409ce50cc0db..19ccf1a55988 100644
> > --- a/net/sched/sch_teql.c
> > +++ b/net/sched/sch_teql.c
> > @@ -16,6 +16,7 @@
> >   #include <linux/skbuff.h>
> >   #include <linux/moduleparam.h>
> >   #include <net/dst.h>
> > +#include <net/ndisc.h>
> >   #include <net/neighbour.h>
> >   #include <net/pkt_sched.h>
> >
> > @@ -257,7 +258,16 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res,
> >       if (dst->dev != dev) {
> >               struct neighbour *mn;
> >
> > -             mn = __neigh_lookup_errno(n->tbl, n->primary_key, dev);
> > +#if IS_ENABLED(CONFIG_IPV6)
> > +             if (n->tbl->family == AF_INET6) {
> > +                     mn = ipv6_neigh_lookup(dev, n->primary_key);
> > +                     if (!mn)
> > +                             mn = ipv6_neigh_create(dev, n->primary_key);
> > +             } else
> > +#endif
> > +             {
> > +                     mn = __neigh_lookup_errno(n->tbl, n->primary_key, dev);
> > +             }
> >               neigh_release(n);
> >               if (IS_ERR(mn))
> >                       return PTR_ERR(mn);
>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 6/9] ipv6: Use ipv6_neigh_lookup() and friends.
  2026-10-06 18:01     ` Kuniyuki Iwashima
@ 2026-10-06 20:34       ` Fernando Fernandez Mancera
  0 siblings, 0 replies; 18+ messages in thread
From: Fernando Fernandez Mancera @ 2026-10-06 20:34 UTC (permalink / raw)
  To: Kuniyuki Iwashima
  Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, David Ahern, Ido Schimmel, Simon Horman,
	Kuniyuki Iwashima, netdev, syzbot+5a8857f0b4a0a7b12c62,
	Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Mark Bloch,
	Petr Machata, Edward Cree, Nikolay Aleksandrov, Alexander Aring,
	Stefan Schmidt, Miquel Raynal

On 10/6/26 8:01 PM, Kuniyuki Iwashima wrote:
> On Tue, Oct 6, 2026 at 10:53 AM Fernando Fernandez Mancera
> <fmancera@suse.de> wrote:
>>
>> On 10/3/26 11:23 PM, Kuniyuki Iwashima wrote:
>>> syzbot reported the splat below in neigh_mark_dead() [0]
>>> where tbl->lock was not held while neigh_ifdown() should
>>> have acquired one.
>>>
>>> This only happens when a neigh_table accidentally has a
>>> neighbour from a different netns.
>>>
>>> In ip6_finish_output2(), the net argument is the caller's and
>>> does not always match dev_net(dev) fetched from dst. (e.g. xfrm)
>>>
>>> It is error-prone to require callers to fetch netns and
>>> neigh_table and pass it with dev to neigh_lookup(), etc.
>>>
>>> Let's replace neigh_lookup() and friends with IPv6 helpers.
>>>
>>> Note that __neigh_lookup() is split into ipv6_neigh_lookup()
>>> and ipv6_neigh_create().
>>>
>>> [0]:
>>> debug_locks && !(lock_is_held(&(&n->tbl->lock)->dep_map) != 0)
>>> WARNING: net/core/neighbour.c:154 at neigh_mark_dead+0x2fb/0x340 net/core/neighbour.c:154, CPU#0: syz.0.857/9765
>>> Modules linked in:
>>> CPU: 0 UID: 0 PID: 9765 Comm: syz.0.857 Not tainted syzkaller #0 PREEMPT(full)
>>> Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/24/2026
>>> RIP: 0010:neigh_mark_dead+0x2fb/0x340 net/core/neighbour.c:154
>>> Code: e8 03 42 80 3c 28 00 74 08 48 89 df e8 ee 9a 80 f8 4c 89 33 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc e8 86 95 10 f8 90 <0f> 0b 90 e9 8f fd ff ff 48 c7 c1 80 9a 7a 90 80 e1 07 80 c1 03 38
>>> RSP: 0018:ffffc90003726600 EFLAGS: 00010287
>>> RAX: ffffffff89b7467a RBX: ffff888022e86000 RCX: 0000000000080000
>>> RDX: ffffc900047fa000 RSI: 0000000000003e08 RDI: 0000000000003e09
>>> RBP: 0000000000000000 R08: 0000000000000003 R09: 0000000000000004
>>> R10: dffffc0000000000 R11: fffff520006e4cb8 R12: ffff888022e8613c
>>> R13: dffffc0000000000 R14: ffff888022e86020 R15: ffff888022e86000
>>> FS:  00007f9c30f046c0(0000) GS:ffff888124cc6000(0000) knlGS:0000000000000000
>>> CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>>> CR2: 0000000000000000 CR3: 0000000076c56000 CR4: 00000000003526f0
>>> Call Trace:
>>>    <TASK>
>>>    neigh_flush_one+0x27c/0x5a0 net/core/neighbour.c:388
>>>    neigh_flush_dev net/core/neighbour.c:432 [inline]
>>>    __neigh_ifdown+0x1fb/0xc70 net/core/neighbour.c:465
>>>    neigh_ifdown+0x1f/0x30 net/core/neighbour.c:487
>>>    rt6_disable_ip+0x751/0x7e0 net/ipv6/route.c:5058
>>>    addrconf_ifdown+0x155/0x1ad0 net/ipv6/addrconf.c:3892
>>>    addrconf_notify+0x1bc/0x1050 net/ipv6/addrconf.c:-1
>>>    notifier_call_chain+0x1a5/0x3d0 kernel/notifier.c:85
>>>    call_netdevice_notifiers_mtu net/core/dev.c:2350 [inline]
>>>    netif_set_mtu_ext+0x5ac/0x830 net/core/dev.c:9963
>>>    do_setlink+0xa81/0x47a0 net/core/rtnetlink.c:3247
>>>    rtnl_setlink+0x578/0x820 net/core/rtnetlink.c:3623
>>>    rtnetlink_rcv_msg+0x802/0xc00 net/core/rtnetlink.c:7159
>>>    netlink_rcv_skb+0x226/0x4a0 net/netlink/af_netlink.c:2572
>>>    netlink_unicast_kernel net/netlink/af_netlink.c:1335 [inline]
>>>    netlink_unicast+0x7bd/0x940 net/netlink/af_netlink.c:1361
>>>    netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1916
>>>    sock_sendmsg_nosec+0x13a/0x180 net/socket.c:800
>>>    __sock_sendmsg net/socket.c:815 [inline]
>>>    sock_write_iter+0x2de/0x3e0 net/socket.c:1266
>>>    do_iter_readv_writev+0x612/0x8c0 fs/read_write.c:-1
>>>    vfs_writev+0x343/0x990 fs/read_write.c:1058
>>>    do_writev+0x154/0x2e0 fs/read_write.c:1104
>>>    do_syscall_x64 arch/x86/emlxsw_sp_ul_rif_getntry/syscall_64.c:61 [inline]
>>>    do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84
>>>    entry_SYSCALL_64_after_hwframe+0x77/0x7f
>>> RIP: 0033:0x7f9c2ff9e159
>>> Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
>>> RSP: 002b:00007f9c30f04028 EFLAGS: 00000246 ORIG_RAX: 0000000000000014
>>> RAX: ffffffffffffffda RBX: 00007f9c30225fa0 RCX: 00007f9c2ff9e159
>>> RDX: 0000000000000001 RSI: 00002000000003c0 RDI: 0000000000000004
>>> RBP: 00007f9c3003506b R08: 0000000000000000 R09: 0000000000000000
>>> R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
>>> R13: 00007f9c30226038 R14: 00007f9c30225fa0 R15: 00007ffed3c74808
>>>    </TASK>
>>>
>>> Fixes: df5f4f372de5 ("ipv6: Replace &nd_tbl with nd_table(net).")
>>> Reported-by: syzbot+5a8857f0b4a0a7b12c62@syzkaller.appspotmail.com
>>> Closes: https://lore.kernel.org/netdev/6abd69a9.80e1c6cc.22483f.0005.GAE@google.com/
>>> Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
>>> ---
>>> v2: Convert __teql_resolve()
>>>
>>> Cc: Saeed Mahameed <saeedm@nvidia.com>
>>> Cc: Leon Romanovsky <leon@kernel.org>
>>> Cc: Tariq Toukan <tariqt@nvidia.com>
>>> Cc: Mark Bloch <mbloch@nvidia.com>
>>> Cc: Petr Machata <petrm@nvidia.com>
>>> Cc: Edward Cree <ecree.xilinx@gmail.com>
>>> Cc: Nikolay Aleksandrov <razor@blackwall.org>
>>> Cc: Alexander Aring <alex.aring@gmail.com>
>>> Cc: Stefan Schmidt <stefan@datenfreihafen.org>
>>> Cc: Miquel Raynal <miquel.raynal@bootlin.com>
>>> ---
>>>    .../mellanox/mlx5/core/en/tc_tun_encap.c      | 13 +++----
>>>    .../ethernet/mellanox/mlxsw/spectrum_router.c | 29 ++++++++-------
>>>    .../ethernet/mellanox/mlxsw/spectrum_span.c   | 24 ++++++++-----
>>>    .../netronome/nfp/flower/tunnel_conf.c        |  4 +--
>>>    drivers/net/ethernet/sfc/tc_counters.c        |  5 ++-
>>>    drivers/net/vrf.c                             |  4 +--
>>>    drivers/net/vxlan/vxlan_core.c                |  6 ++--
>>>    include/net/ndisc.h                           |  7 ++--
>>>    net/bridge/br_arp_nd_proxy.c                  |  2 +-
>>>    net/ieee802154/6lowpan/tx.c                   |  3 +-
>>>    net/ipv4/fib_semantics.c                      |  2 +-
>>>    net/ipv6/ip6_output.c                         |  2 +-
>>>    net/ipv6/ndisc.c                              | 36 ++++++++++++-------
>>>    net/ipv6/route.c                              | 11 +++---
>>>    net/sched/sch_teql.c                          | 12 ++++++-
>>>    15 files changed, 90 insertions(+), 70 deletions(-)
>>>
>>> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
>>> index 67c12ca19d59..fe0258375ef6 100644
>>> --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
>>> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
>>> @@ -439,21 +439,18 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
>>>        if (neigh_used) {
>>>                struct net_device *dev = READ_ONCE(nhe->neigh_dev);
>>>                struct net *net = dev_net(dev);
>>> -             struct neigh_table *tbl;
>>>
>>>                nhe->reported_lastuse = jiffies;
>>>
>>> +             /* find the relevant neigh according to the cached device and
>>> +              * dst ip pair
>>> +              */
>>>    #if IS_ENABLED(CONFIG_IPV6)
>>>                if (m_neigh->family != AF_INET)
>>> -                     tbl = nd_table(net);
>>> +                     n = ipv6_neigh_lookup(dev, &m_neigh->dst_ip);
>>>                else
>>>    #endif
>>> -                     tbl = arp_table(net);
>>> -
>>> -             /* find the relevant neigh according to the cached device and
>>> -              * dst ip pair
>>> -              */
>>> -             n = neigh_lookup(tbl, &m_neigh->dst_ip, dev);
>>> +                     n = neigh_lookup(arp_table(net), &m_neigh->dst_ip, dev);
>>>                if (!n)
>>>                        return;
>>>
>>> diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
>>> index ba8a7a44ce9e..1f4753213b9c 100644
>>> --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
>>> +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
>>> @@ -2449,8 +2449,6 @@ static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
>>>                                                   char *rauhtd_pl,
>>>                                                   int rec_index)
>>>    {
>>> -     struct net *net = mlxsw_sp_net(mlxsw_sp);
>>> -     struct neigh_table *tbl;
>>>        struct net_device *dev;
>>>        struct neighbour *n;
>>>        struct in6_addr dip;
>>> @@ -2464,9 +2462,8 @@ static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
>>>                return;
>>>        }
>>>
>>> -     tbl = nd_table(net);
>>>        dev = mlxsw_sp_rif_dev(mlxsw_sp->router->rifs[rif]);
>>> -     n = neigh_lookup(tbl, &dip, dev);
>>> +     n = ipv6_neigh_lookup(dev, &dip);
>>
>>
>> Hi Kuniyuki,
>>
>> I have checked Sashiko's feedback [0] and I think it is right.
> 
> I think this is false-positive.
> 
> This was pointed out in v1 too, but I did not add NULL check to avoid
> defensive programming.
> 
> Hardware only reports neighbour activity via RAUHTD for entries programmed
> into RAUHT by mlxsw_sp_neigh_entry_update().  Those entries are created only
> in mlxsw_sp_neigh_entry_create(), which resolves the RIF via
> mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev) where n->dev is always non-NULL.
> Thus, underlay and loopback RIFs (where mlxsw_sp_rif_dev() is NULL) never
> have neighbour entries programmed in hardware.
> 
> Also, when a RIF is destroyed in mlxsw_sp_rif_destroy(), all of its
> RAUHT entries are synchronously removed from hardware in
> mlxsw_sp_neigh_rif_gone_sync() and router->rifs[rif] is cleared under
> router->lock (the same lock held during the RAUHTD dump) before the RIF
> index can be reused.
> 

Sorry I missed the mlxsw_sp_rif_find_by_dev() call. You are right here. 
Thanks a lot for explaining!

> 
>>
>> mlxsw_sp_router_ul_rif_get() can be called with a NULL mlxsw_sp_crif
>> pointer which then would call mlxsw_sp_ul_rif_create() and there during
>> the alloc a rif with a NULL crif is created making this feedback being
>> correct.
>>
>> I think a simple NULL check here for dev should be enough.
>>
>> [0]
>> https://sashiko.dev/#/message/20261003212336.1304988-7-kuniyu%40google.com
> 
> NIPA's Sashiko is pretty better than Gemini 3.1 these days.
> I hope our instance support Gemini 4 soon :)
> https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261003212336.1304988-1-kuniyu%40google.com
> 
> 
>>
>> Thanks!
>>
>>>        if (!n)
>>>                return;
>>>
>>> @@ -4312,17 +4309,23 @@ mlxsw_sp_nexthop_neigh_lookup(struct mlxsw_sp_nexthop *nh)
>>>        net = dev_net(dev);
>>>
>>>    #if IS_ENABLED(CONFIG_IPV6)
>>> -     if (nh->family == AF_INET6)
>>> -             tbl = nd_table(net);
>>> -     else
>>> +     if (nh->family == AF_INET6) {
>>> +             n = ipv6_neigh_lookup(dev, &nh->gw_addr);
>>> +             if (!n) {
>>> +                     n = ipv6_neigh_create(dev, &nh->gw_addr);
>>> +                     if (!IS_ERR(n))
>>> +                             neigh_event_send(n, NULL);
>>> +             }
>>> +     } else
>>>    #endif
>>> +     {
>>>                tbl = arp_table(net);
>>> -
>>> -     n = neigh_lookup(tbl, &nh->gw_addr, dev);
>>> -     if (!n) {
>>> -             n = neigh_create(tbl, &nh->gw_addr, dev);
>>> -             if (!IS_ERR(n))
>>> -                     neigh_event_send(n, NULL);
>>> +             n = neigh_lookup(tbl, &nh->gw_addr, dev);
>>> +             if (!n) {
>>> +                     n = neigh_create(tbl, &nh->gw_addr, dev);
>>> +                     if (!IS_ERR(n))
>>> +                             neigh_event_send(n, NULL);
>>> +             }
>>>        }
>>>
>>>        return n;
>>> diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
>>> index d34d2040177b..79947f68b10d 100644
>>> --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
>>> +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
>>> @@ -235,17 +235,23 @@ static int mlxsw_sp_span_dmac(int family,
>>>        int err = 0;
>>>
>>>    #if IS_ENABLED(CONFIG_IPV6_GRE)
>>> -     if (family == AF_INET6)
>>> -             tbl = nd_table(net);
>>> -     else
>>> +     if (family == AF_INET6) {
>>> +             neigh = ipv6_neigh_lookup(dev, pkey);
>>> +             if (!neigh) {
>>> +                     neigh = ipv6_neigh_create(dev, pkey);
>>> +                     if (IS_ERR(neigh))
>>> +                             return PTR_ERR(neigh);
>>> +             }
>>> +     } else
>>>    #endif
>>> +     {
>>>                tbl = arp_table(net);
>>> -
>>> -     neigh = neigh_lookup(tbl, pkey, dev);
>>> -     if (!neigh) {
>>> -             neigh = neigh_create(tbl, pkey, dev);
>>> -             if (IS_ERR(neigh))
>>> -                     return PTR_ERR(neigh);
>>> +             neigh = neigh_lookup(tbl, pkey, dev);
>>> +             if (!neigh) {
>>> +                     neigh = neigh_create(tbl, pkey, dev);
>>> +                     if (IS_ERR(neigh))
>>> +                             return PTR_ERR(neigh);
>>> +             }
>>>        }
>>>
>>>        neigh_event_send(neigh, NULL);
>>> diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
>>> index d650d33e3709..27d80ec8e895 100644
>>> --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
>>> +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
>>> @@ -253,7 +253,6 @@ void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb)
>>>    #if IS_ENABLED(CONFIG_IPV6)
>>>        struct nfp_tun_active_tuns_v6 *payload;
>>>        struct net_device *netdev;
>>> -     struct neigh_table *tbl;
>>>        int count, i, pay_len;
>>>        struct neighbour *n;
>>>        void *ipv6_add;
>>> @@ -280,8 +279,7 @@ void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb)
>>>                if (!netdev)
>>>                        continue;
>>>
>>> -             tbl = nd_table(dev_net(netdev));
>>> -             n = neigh_lookup(tbl, ipv6_add, netdev);
>>> +             n = ipv6_neigh_lookup(netdev, ipv6_add);
>>>                if (!n)
>>>                        continue;
>>>
>>> diff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c
>>> index 793a562fc1f7..ae6cc6b93864 100644
>>> --- a/drivers/net/ethernet/sfc/tc_counters.c
>>> +++ b/drivers/net/ethernet/sfc/tc_counters.c
>>> @@ -116,9 +116,8 @@ static void efx_tc_counter_work(struct work_struct *work)
>>>                                         encap->neigh->egdev);
>>>                else
>>>    #if IS_ENABLED(CONFIG_IPV6)
>>> -                     n = neigh_lookup(nd_table(net),
>>> -                                      &encap->neigh->dst_ip6,
>>> -                                      encap->neigh->egdev);
>>> +                     n = ipv6_neigh_lookup(encap->neigh->egdev,
>>> +                                           &encap->neigh->dst_ip6);
>>>    #else
>>>                        n = NULL;
>>>    #endif
>>> diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
>>> index 320f3584a43b..79d433f49f80 100644
>>> --- a/drivers/net/vrf.c
>>> +++ b/drivers/net/vrf.c
>>> @@ -614,9 +614,9 @@ static int vrf_finish_output6(struct net *net, struct sock *sk,
>>>
>>>        rcu_read_lock();
>>>        nexthop = rt6_nexthop(dst_rt6_info(dst), &ipv6_hdr(skb)->daddr);
>>> -     neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
>>> +     neigh = __ipv6_neigh_lookup_noref(dev, nexthop);
>>>        if (unlikely(!neigh))
>>> -             neigh = __neigh_create(nd_table(net), nexthop, dst->dev, false);
>>> +             neigh = ipv6_neigh_create_noref(dev, nexthop);
>>>        if (!IS_ERR(neigh)) {
>>>                sock_confirm_neigh(skb, neigh);
>>>                ret = neigh_output(neigh, skb, false);
>>> diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
>>> index 27b0b6567d52..513779c07621 100644
>>> --- a/drivers/net/vxlan/vxlan_core.c
>>> +++ b/drivers/net/vxlan/vxlan_core.c
>>> @@ -2091,8 +2091,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb,
>>>            ipv6_addr_is_multicast(&msg->target))
>>>                goto out;
>>>
>>> -     n = neigh_lookup(nd_table(dev_net(dev)), &msg->target, dev);
>>> -
>>> +     n = ipv6_neigh_lookup(dev, &msg->target);
>>>        if (n) {
>>>                struct vxlan_rdst *rdst = NULL;
>>>                u8 ha[ETH_ALEN] __aligned(2);
>>> @@ -2189,9 +2188,8 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb,
>>>                if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
>>>                        return false;
>>>
>>> -             tbl = nd_table(dev_net(dev));
>>>                pip6 = ipv6_hdr(skb);
>>> -             n = neigh_lookup(tbl, &pip6->daddr, dev);
>>> +             n = ipv6_neigh_lookup(dev, &pip6->daddr);
>>>                if (!n && (cfg->flags & VXLAN_F_L3MISS)) {
>>>                        union vxlan_addr ipa = {
>>>                                .sin6.sin6_addr = pip6->daddr,
>>> diff --git a/include/net/ndisc.h b/include/net/ndisc.h
>>> index 2fce6fccf55a..91898a2475e7 100644
>>> --- a/include/net/ndisc.h
>>> +++ b/include/net/ndisc.h
>>> @@ -423,11 +423,8 @@ static inline struct neighbour *ip_neigh_gw6(struct net_device *dev,
>>>        struct neighbour *neigh;
>>>
>>>        neigh = __ipv6_neigh_lookup_noref(dev, addr);
>>> -     if (unlikely(!neigh)) {
>>> -             struct neigh_table *tbl = nd_table(dev_net(dev));
>>> -
>>> -             neigh = __neigh_create(tbl, addr, dev, false);
>>> -     }
>>> +     if (unlikely(!neigh))
>>> +             neigh = ipv6_neigh_create_noref(dev, addr);
>>>
>>>        return neigh;
>>>    #else
>>> diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
>>> index ba4a63840b21..c23b99517cd5 100644
>>> --- a/net/bridge/br_arp_nd_proxy.c
>>> +++ b/net/bridge/br_arp_nd_proxy.c
>>> @@ -487,7 +487,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
>>>                return;
>>>        }
>>>
>>> -     n = neigh_lookup(nd_table(dev_net(vlandev)), &msg->target, vlandev);
>>> +     n = ipv6_neigh_lookup(vlandev, &msg->target);
>>>        if (n) {
>>>                struct net_bridge_fdb_entry *f;
>>>                u8 ha[ETH_ALEN] __aligned(2);
>>> diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c
>>> index 4f511476b992..b261daedc290 100644
>>> --- a/net/ieee802154/6lowpan/tx.c
>>> +++ b/net/ieee802154/6lowpan/tx.c
>>> @@ -58,9 +58,8 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
>>>                info->daddr.mode = IEEE802154_ADDR_SHORT;
>>>        } else {
>>>                __le16 short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
>>> -             struct neigh_table *tbl = nd_table(dev_net(ldev));
>>>
>>> -             n = neigh_lookup(tbl, &hdr->daddr, ldev);
>>> +             n = ipv6_neigh_lookup(ldev, &hdr->daddr);
>>>                if (n) {
>>>                        llneigh = lowpan_802154_neigh(neighbour_priv(n));
>>>                        read_lock_bh(&n->lock);
>>> diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
>>> index e3bcc25229b0..a98c7670d2ce 100644
>>> --- a/net/ipv4/fib_semantics.c
>>> +++ b/net/ipv4/fib_semantics.c
>>> @@ -617,7 +617,7 @@ static int fib_detect_death(struct fib_info *fi, int order,
>>>        if (likely(nhc->nhc_gw_family == AF_INET))
>>>                n = neigh_lookup(arp_table(net), &nhc->nhc_gw.ipv4, nhc->nhc_dev);
>>>        else if (IS_ENABLED(CONFIG_IPV6) && nhc->nhc_gw_family == AF_INET6)
>>> -             n = neigh_lookup(nd_table(net), &nhc->nhc_gw.ipv6, nhc->nhc_dev);
>>> +             n = ipv6_neigh_lookup(nhc->nhc_dev, &nhc->nhc_gw.ipv6);
>>>        else
>>>                n = NULL;
>>>
>>> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
>>> index 10146a57b69e..25fe0ba98b1a 100644
>>> --- a/net/ipv6/ip6_output.c
>>> +++ b/net/ipv6/ip6_output.c
>>> @@ -127,7 +127,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
>>>
>>>        if (IS_ERR_OR_NULL(neigh)) {
>>>                if (unlikely(!neigh))
>>> -                     neigh = __neigh_create(nd_table(net), nexthop, dev, false);
>>> +                     neigh = ipv6_neigh_create_noref(dev, nexthop);
>>>                if (IS_ERR(neigh)) {
>>>                        IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTNOROUTES);
>>>                        kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_CREATEFAIL);
>>> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
>>> index e1cbffaa0ad0..0ed1a619372b 100644
>>> --- a/net/ipv6/ndisc.c
>>> +++ b/net/ipv6/ndisc.c
>>> @@ -947,8 +947,12 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
>>>         *      update / create cache entry
>>>         *      for the source address
>>>         */
>>> -     neigh = __neigh_lookup(tbl, saddr, dev,
>>> -                            !inc || lladdr || !dev->addr_len);
>>> +     neigh = ipv6_neigh_lookup(dev, saddr);
>>> +     if (!neigh && (!inc || lladdr || !dev->addr_len)) {
>>> +             neigh = ipv6_neigh_create(dev, saddr);
>>> +             if (IS_ERR(neigh))
>>> +                     neigh = NULL;
>>> +     }
>>>        if (neigh)
>>>                ndisc_update(dev, neigh, lladdr, NUD_STALE,
>>>                             NEIGH_UPDATE_F_WEAK_OVERRIDE|
>>> @@ -998,7 +1002,6 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
>>>        struct net *net = dev_net(dev);
>>>        struct ndisc_options ndopts;
>>>        struct inet6_ifaddr *ifp;
>>> -     struct neigh_table *tbl;
>>>        struct neighbour *neigh;
>>>        struct inet6_dev *idev;
>>>        u8 *lladdr = NULL;
>>> @@ -1063,8 +1066,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
>>>                return reason;
>>>        }
>>>
>>> -     tbl = nd_table(net);
>>> -     neigh = neigh_lookup(tbl, &msg->target, dev);
>>> +     neigh = ipv6_neigh_lookup(dev, &msg->target);
>>>
>>>        /* RFC 9131 updates original Neighbour Discovery RFC 4861.
>>>         * NAs with Target LL Address option can now create a STALE neighbor
>>> @@ -1093,7 +1095,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
>>>                        return reason;
>>>                }
>>>                if (!neigh)
>>> -                     neigh = neigh_create(tbl, &msg->target, dev);
>>> +                     neigh = ipv6_neigh_create(dev, &msg->target);
>>>                new_state = NUD_STALE;
>>>        }
>>>
>>> @@ -1108,7 +1110,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(tbl, &msg->target, dev)) {
>>> +                 pneigh_lookup(nd_table(dev_net(dev)), &msg->target, dev)) {
>>>                        /* XXX: idev->cnf.proxy_ndp */
>>>                        goto out;
>>>                }
>>> @@ -1141,7 +1143,6 @@ static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
>>>        unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
>>>        struct net_device *dev = skb->dev;
>>>        struct ndisc_options ndopts;
>>> -     struct neigh_table *tbl;
>>>        struct neighbour *neigh;
>>>        struct inet6_dev *idev;
>>>        u8 *lladdr = NULL;
>>> @@ -1177,8 +1178,12 @@ static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
>>>                        goto out;
>>>        }
>>>
>>> -     tbl = nd_table(dev_net(dev));
>>> -     neigh = __neigh_lookup(tbl, saddr, dev, 1);
>>> +     neigh = ipv6_neigh_lookup(dev, saddr);
>>> +     if (!neigh) {
>>> +             neigh = ipv6_neigh_create(dev, saddr);
>>> +             if (IS_ERR(neigh))
>>> +                     goto out;
>>> +     }
>>>        if (neigh) {
>>>                ndisc_update(dev, neigh, lladdr, NUD_STALE,
>>>                             NEIGH_UPDATE_F_WEAK_OVERRIDE|
>>> @@ -1478,9 +1483,14 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
>>>         *      Process options.
>>>         */
>>>
>>> -     if (!neigh)
>>> -             neigh = __neigh_lookup(nd_table(net), &ipv6_hdr(skb)->saddr,
>>> -                                    skb->dev, 1);
>>> +     if (!neigh) {
>>> +             neigh = ipv6_neigh_lookup(skb->dev, &ipv6_hdr(skb)->saddr);
>>> +             if (!neigh) {
>>> +                     neigh = ipv6_neigh_create(skb->dev, &ipv6_hdr(skb)->saddr);
>>> +                     if (IS_ERR(neigh))
>>> +                             neigh = NULL;
>>> +             }
>>> +     }
>>>        if (neigh) {
>>>                u8 *lladdr = NULL;
>>>                if (ndopts.nd_opts_src_lladdr) {
>>> diff --git a/net/ipv6/route.c b/net/ipv6/route.c
>>> index 8baac4d85251..ea9f0a4c34f9 100644
>>> --- a/net/ipv6/route.c
>>> +++ b/net/ipv6/route.c
>>> @@ -218,7 +218,7 @@ struct neighbour *__ip6_dst_neigh_lookup(const struct in6_addr *gw,
>>>        if (n)
>>>                return n;
>>>
>>> -     n = neigh_create(nd_table(dev_net(dev)), daddr, dev);
>>> +     n = ipv6_neigh_create(dev, daddr);
>>>        return IS_ERR(n) ? NULL : n;
>>>    }
>>>
>>> @@ -4277,9 +4277,12 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
>>>         */
>>>        dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
>>>
>>> -     neigh = __neigh_lookup(nd_table(dev_net(dev)), &msg->target, dev, 1);
>>> -     if (!neigh)
>>> -             return;
>>> +     neigh = ipv6_neigh_lookup(dev, &msg->target);
>>> +     if (!neigh) {
>>> +             neigh = ipv6_neigh_create(dev, &msg->target);
>>> +             if (IS_ERR(neigh))
>>> +                     return;
>>> +     }
>>>
>>>        /*
>>>         *      We have finally decided to accept it.
>>> diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
>>> index 409ce50cc0db..19ccf1a55988 100644
>>> --- a/net/sched/sch_teql.c
>>> +++ b/net/sched/sch_teql.c
>>> @@ -16,6 +16,7 @@
>>>    #include <linux/skbuff.h>
>>>    #include <linux/moduleparam.h>
>>>    #include <net/dst.h>
>>> +#include <net/ndisc.h>
>>>    #include <net/neighbour.h>
>>>    #include <net/pkt_sched.h>
>>>
>>> @@ -257,7 +258,16 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res,
>>>        if (dst->dev != dev) {
>>>                struct neighbour *mn;
>>>
>>> -             mn = __neigh_lookup_errno(n->tbl, n->primary_key, dev);
>>> +#if IS_ENABLED(CONFIG_IPV6)
>>> +             if (n->tbl->family == AF_INET6) {
>>> +                     mn = ipv6_neigh_lookup(dev, n->primary_key);
>>> +                     if (!mn)
>>> +                             mn = ipv6_neigh_create(dev, n->primary_key);
>>> +             } else
>>> +#endif
>>> +             {
>>> +                     mn = __neigh_lookup_errno(n->tbl, n->primary_key, dev);
>>> +             }
>>>                neigh_release(n);
>>>                if (IS_ERR(mn))
>>>                        return PTR_ERR(mn);
>>


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends.
  2026-10-03 21:23 [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Kuniyuki Iwashima
                   ` (8 preceding siblings ...)
  2026-10-03 21:23 ` [PATCH v2 net-next 9/9] neighbour: Check n->parms->tbl under tbl->lock in ___neigh_create() Kuniyuki Iwashima
@ 2026-10-06 20:39 ` Fernando Fernandez Mancera
  2026-10-07 16:35 ` Jakub Kicinski
  2026-10-07 23:20 ` patchwork-bot+netdevbpf
  11 siblings, 0 replies; 18+ messages in thread
From: Fernando Fernandez Mancera @ 2026-10-06 20:39 UTC (permalink / raw)
  To: Kuniyuki Iwashima, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel
  Cc: Simon Horman, Kuniyuki Iwashima, netdev

On 10/3/26 11:23 PM, Kuniyuki Iwashima wrote:
> syzbot reported that ip6_finish_output2() could pass tbl and dev
> from different netns to neigh_create().
> 
> When namespacifying neigh_table, I chose to resolve neigh_table
> in callers to minimise changes, but it turned out to be error-prone.
> 
> This series adds IPv4/IPv6-specific helpers for neigh_lookup()
> and neigh_create() that always fetch the netns from dev to fix
> the problem.
> 
> Along the way, the confusing and now mostly redundant helpers
> __neigh_lookup() and __neigh_lookup_errno() are converted and
> removed.
> 

Reviewed-by: Fernando Fernandez Mancera <fmancera@suse.de>

Thanks!

> 
> Changes:
>    v2:
>      * Add Patch 9
>      * Patch 6 & 7: Convert __teql_resolve()
> 
>    v1: https://lore.kernel.org/netdev/20260930200326.718459-1-kuniyu@google.com/
> 
> 
> Kuniyuki Iwashima (9):
>    ipv4: Rename ipv4_neigh_lookup() to ipv4_dst_neigh_lookup().
>    ipv6: Rename ip6_neigh_lookup() to __ip6_dst_neigh_lookup().
>    ipv4: Add wrappers for neigh_lookup() and neigh_create().
>    ipv6: Add wrappers for neigh_lookup() and (__)?neigh_create().
>    mlxsw: spectrum: Resolve neigh_table right before neigh_lookup().
>    ipv6: Use ipv6_neigh_lookup() and friends.
>    ipv4: Use ipv4_neigh_lookup() and friends.
>    neighbour: Remove __neigh_lookup() and __neigh_lookup_errno().
>    neighbour: Check n->parms->tbl under tbl->lock in ___neigh_create().
> 
>   .../marvell/prestera/prestera_router.c        | 14 ++--
>   .../mellanox/mlx5/core/en/tc_tun_encap.c      | 14 ++--
>   .../mellanox/mlx5/core/en_accel/ipsec.c       |  6 +-
>   .../ethernet/mellanox/mlxsw/spectrum_router.c | 77 +++++++++++--------
>   .../ethernet/mellanox/mlxsw/spectrum_span.c   | 42 ++++++----
>   .../netronome/nfp/flower/tunnel_conf.c        |  8 +-
>   drivers/net/ethernet/rocker/rocker_ofdpa.c    |  2 +-
>   drivers/net/ethernet/sfc/tc_counters.c        | 11 +--
>   drivers/net/vrf.c                             |  4 +-
>   drivers/net/vxlan/vxlan_core.c                | 14 +---
>   include/net/arp.h                             | 16 ++++
>   include/net/ip6_route.h                       |  6 +-
>   include/net/ndisc.h                           | 31 ++++++--
>   include/net/neighbour.h                       | 24 ------
>   net/bridge/br_arp_nd_proxy.c                  |  4 +-
>   net/core/neighbour.c                          | 11 ++-
>   net/ieee802154/6lowpan/tx.c                   |  3 +-
>   net/ipv4/arp.c                                | 26 ++++---
>   net/ipv4/fib_semantics.c                      |  5 +-
>   net/ipv4/route.c                              | 18 ++---
>   net/ipv6/ip6_output.c                         |  2 +-
>   net/ipv6/ndisc.c                              | 48 +++++++-----
>   net/ipv6/route.c                              | 23 +++---
>   net/sched/sch_teql.c                          | 15 +++-
>   24 files changed, 234 insertions(+), 190 deletions(-)
> 


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends.
  2026-10-03 21:23 [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Kuniyuki Iwashima
                   ` (9 preceding siblings ...)
  2026-10-06 20:39 ` [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Fernando Fernandez Mancera
@ 2026-10-07 16:35 ` Jakub Kicinski
  2026-10-07 16:50   ` Ido Schimmel
  2026-10-07 23:20 ` patchwork-bot+netdevbpf
  11 siblings, 1 reply; 18+ messages in thread
From: Jakub Kicinski @ 2026-10-07 16:35 UTC (permalink / raw)
  To: David Ahern, Ido Schimmel
  Cc: Kuniyuki Iwashima, Andrew Lunn, David S . Miller, Eric Dumazet,
	Paolo Abeni, Simon Horman, Kuniyuki Iwashima, netdev

On Sat,  3 Oct 2026 21:23:13 +0000 Kuniyuki Iwashima wrote:
> syzbot reported that ip6_finish_output2() could pass tbl and dev
> from different netns to neigh_create().
> 
> When namespacifying neigh_table, I chose to resolve neigh_table
> in callers to minimise changes, but it turned out to be error-prone.
> 
> This series adds IPv4/IPv6-specific helpers for neigh_lookup()
> and neigh_create() that always fetch the netns from dev to fix
> the problem.
> 
> Along the way, the confusing and now mostly redundant helpers
> __neigh_lookup() and __neigh_lookup_errno() are converted and
> removed.

Ido, David, thoughts?
The unusual (for Ido) silence makes me suspicious :)

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends.
  2026-10-07 16:35 ` Jakub Kicinski
@ 2026-10-07 16:50   ` Ido Schimmel
  2026-10-07 18:20     ` Jakub Kicinski
  0 siblings, 1 reply; 18+ messages in thread
From: Ido Schimmel @ 2026-10-07 16:50 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: David Ahern, Kuniyuki Iwashima, Andrew Lunn, David S . Miller,
	Eric Dumazet, Paolo Abeni, Simon Horman, Kuniyuki Iwashima,
	netdev

On Wed, Oct 07, 2026 at 09:35:55AM -0700, Jakub Kicinski wrote:
> On Sat,  3 Oct 2026 21:23:13 +0000 Kuniyuki Iwashima wrote:
> > syzbot reported that ip6_finish_output2() could pass tbl and dev
> > from different netns to neigh_create().
> > 
> > When namespacifying neigh_table, I chose to resolve neigh_table
> > in callers to minimise changes, but it turned out to be error-prone.
> > 
> > This series adds IPv4/IPv6-specific helpers for neigh_lookup()
> > and neigh_create() that always fetch the netns from dev to fix
> > the problem.
> > 
> > Along the way, the confusing and now mostly redundant helpers
> > __neigh_lookup() and __neigh_lookup_errno() are converted and
> > removed.
> 
> Ido, David, thoughts?
> The unusual (for Ido) silence makes me suspicious :)

Too many other patches to review...

The series looks good to me:

Reviewed-by: Ido Schimmel <idosch@nvidia.com>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends.
  2026-10-07 16:50   ` Ido Schimmel
@ 2026-10-07 18:20     ` Jakub Kicinski
  0 siblings, 0 replies; 18+ messages in thread
From: Jakub Kicinski @ 2026-10-07 18:20 UTC (permalink / raw)
  To: Ido Schimmel
  Cc: David Ahern, Kuniyuki Iwashima, Andrew Lunn, David S . Miller,
	Eric Dumazet, Paolo Abeni, Simon Horman, Kuniyuki Iwashima,
	netdev

On Wed, 7 Oct 2026 19:50:00 +0300 Ido Schimmel wrote:
> > Ido, David, thoughts?
> > The unusual (for Ido) silence makes me suspicious :)  
> 
> Too many other patches to review...

FWIW you may be able to find some use of SUIE with the MAINTAINER
filter (funnel icon)
https://netdev-ctrl.bots.linux.dev/suie.html?delegate=netdev

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends.
  2026-10-03 21:23 [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Kuniyuki Iwashima
                   ` (10 preceding siblings ...)
  2026-10-07 16:35 ` Jakub Kicinski
@ 2026-10-07 23:20 ` patchwork-bot+netdevbpf
  11 siblings, 0 replies; 18+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-10-07 23:20 UTC (permalink / raw)
  To: Kuniyuki Iwashima
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, dsahern, idosch,
	horms, kuni1840, netdev

Hello:

This series was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Sat,  3 Oct 2026 21:23:13 +0000 you wrote:
> syzbot reported that ip6_finish_output2() could pass tbl and dev
> from different netns to neigh_create().
> 
> When namespacifying neigh_table, I chose to resolve neigh_table
> in callers to minimise changes, but it turned out to be error-prone.
> 
> This series adds IPv4/IPv6-specific helpers for neigh_lookup()
> and neigh_create() that always fetch the netns from dev to fix
> the problem.
> 
> [...]

Here is the summary with links:
  - [v2,net-next,1/9] ipv4: Rename ipv4_neigh_lookup() to ipv4_dst_neigh_lookup().
    https://git.kernel.org/netdev/net-next/c/07cc2b151a26
  - [v2,net-next,2/9] ipv6: Rename ip6_neigh_lookup() to __ip6_dst_neigh_lookup().
    https://git.kernel.org/netdev/net-next/c/3e33cdcf54ee
  - [v2,net-next,3/9] ipv4: Add wrappers for neigh_lookup() and neigh_create().
    https://git.kernel.org/netdev/net-next/c/33ea2463c432
  - [v2,net-next,4/9] ipv6: Add wrappers for neigh_lookup() and (__)?neigh_create().
    https://git.kernel.org/netdev/net-next/c/616b2143f901
  - [v2,net-next,5/9] mlxsw: spectrum: Resolve neigh_table right before neigh_lookup().
    https://git.kernel.org/netdev/net-next/c/98071355ffc9
  - [v2,net-next,6/9] ipv6: Use ipv6_neigh_lookup() and friends.
    https://git.kernel.org/netdev/net-next/c/0e59523b440c
  - [v2,net-next,7/9] ipv4: Use ipv4_neigh_lookup() and friends.
    https://git.kernel.org/netdev/net-next/c/f2f25d59f56a
  - [v2,net-next,8/9] neighbour: Remove __neigh_lookup() and __neigh_lookup_errno().
    https://git.kernel.org/netdev/net-next/c/29ff0101dc00
  - [v2,net-next,9/9] neighbour: Check n->parms->tbl under tbl->lock in ___neigh_create().
    https://git.kernel.org/netdev/net-next/c/023753192e3c

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2026-10-07 23:20 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-03 21:23 [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Kuniyuki Iwashima
2026-10-03 21:23 ` [PATCH v2 net-next 1/9] ipv4: Rename ipv4_neigh_lookup() to ipv4_dst_neigh_lookup() Kuniyuki Iwashima
2026-10-03 21:23 ` [PATCH v2 net-next 2/9] ipv6: Rename ip6_neigh_lookup() to __ip6_dst_neigh_lookup() Kuniyuki Iwashima
2026-10-03 21:23 ` [PATCH v2 net-next 3/9] ipv4: Add wrappers for neigh_lookup() and neigh_create() Kuniyuki Iwashima
2026-10-03 21:23 ` [PATCH v2 net-next 4/9] ipv6: Add wrappers for neigh_lookup() and (__)?neigh_create() Kuniyuki Iwashima
2026-10-03 21:23 ` [PATCH v2 net-next 5/9] mlxsw: spectrum: Resolve neigh_table right before neigh_lookup() Kuniyuki Iwashima
2026-10-03 21:23 ` [PATCH v2 net-next 6/9] ipv6: Use ipv6_neigh_lookup() and friends Kuniyuki Iwashima
2026-10-06 17:53   ` Fernando Fernandez Mancera
2026-10-06 18:01     ` Kuniyuki Iwashima
2026-10-06 20:34       ` Fernando Fernandez Mancera
2026-10-03 21:23 ` [PATCH v2 net-next 7/9] ipv4: Use ipv4_neigh_lookup() " Kuniyuki Iwashima
2026-10-03 21:23 ` [PATCH v2 net-next 8/9] neighbour: Remove __neigh_lookup() and __neigh_lookup_errno() Kuniyuki Iwashima
2026-10-03 21:23 ` [PATCH v2 net-next 9/9] neighbour: Check n->parms->tbl under tbl->lock in ___neigh_create() Kuniyuki Iwashima
2026-10-06 20:39 ` [PATCH v2 net-next 0/9] neighbour: Add IPv4/IPv6-specific wrappers for neigh_lookup() and friends Fernando Fernandez Mancera
2026-10-07 16:35 ` Jakub Kicinski
2026-10-07 16:50   ` Ido Schimmel
2026-10-07 18:20     ` Jakub Kicinski
2026-10-07 23:20 ` patchwork-bot+netdevbpf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox