[PATCH net-next 3/5] bridge: Validate NS/NA messages using ndisc_check_ns_na()
From: Danielle Ratson <hidden>
Date: 2026-07-19 13:35:46
Also in:
bridge, lkml
Subsystem:
ethernet bridge, networking [general], the rest · Maintainers:
Nikolay Aleksandrov, Ido Schimmel, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
The bridge performs neighbor suppression by snooping NS/NA messages, but previously only checked the ICMPv6 type and code. This leaves it open to acting on malformed or spoofed packets that any RFC-compliant node should reject. Wire br_is_nd_neigh_msg() into the new ndisc_check_ns_na() helper, which enforces the full RFC 4861 section 7.1.1/7.1.2 receive validation: hop limit of 255, valid checksum, correct code, and type-specific rules (NS target not multicast; NA solicited flag clear for multicast destinations). MLD messages are already validated by ipv6_mc_check_mld() before the bridge acts on them; this brings NS/NA to the same standard. As a side effect, the skb parameter of br_is_nd_neigh_msg() changes from const to non-const, since ndisc_check_ns_na() may reallocate the skb head via pskb_may_pull() and sets the transport header. The returned pointer is now derived from skb_transport_header() rather than a direct cast. Reviewed-by: Petr Machata <petrm@nvidia.com> Signed-off-by: Danielle Ratson <redacted> --- net/bridge/br_arp_nd_proxy.c | 11 ++++------- net/bridge/br_private.h | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
index db08c3272001..445c930ed59b 100644
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c@@ -19,6 +19,7 @@ #include <net/addrconf.h> #if IS_ENABLED(CONFIG_IPV6) #include <net/ip6_checksum.h> +#include <net/ndisc.h> #endif #include "br_private.h"
@@ -234,16 +235,12 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, #endif #if IS_ENABLED(CONFIG_IPV6) -struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb) +struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb) { - struct nd_msg *m = (struct nd_msg *)(ipv6_hdr(skb) + 1); - - if (m->icmph.icmp6_code != 0 || - (m->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION && - m->icmph.icmp6_type != NDISC_NEIGHBOUR_ADVERTISEMENT)) + if (ndisc_check_ns_na(skb)) return NULL; - return m; + return (struct nd_msg *)skb_transport_header(skb); } static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index a51246747537..81b807487dcc 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h@@ -2365,7 +2365,7 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, u16 vid, struct net_bridge_port *p); void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, u16 vid, struct net_bridge_port *p, struct nd_msg *msg); -struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb); +struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb); bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid); bool br_is_neigh_forward_grat_enabled(const struct net_bridge_port *p, u16 vid); #endif
--
2.54.0