The existing neighbor suppression unconditionally suppresses gratuitous ARPs and unsolicited Neighbor Advertisements, which prevents fast mobility of hosts between VTEPs. Add the neigh_forward_grat option to allow selective control of gratuitous neighbor announcements. When neigh_suppress is enabled but neigh_forward_grat is disabled (default), gratuitous announcements are suppressed. When neigh_forward_grat is enabled, gratuitous announcements are forwarded while regular neighbor discovery remains suppressed. The implementation provides per-output-port control by: 1. Adding a 'grat_arp' flag to BR_INPUT_SKB_CB to mark gratuitous ARPs and unsolicited NAs. 2. Setting both grat_arp and proxyarp_replied flags in br_do_proxy_suppress_arp() and br_do_suppress_nd() when gratuitous packets are detected. 3. Checking neigh_forward_grat per output port during flooding: - For gratuitous ARPs/NAs: suppress unless the output port has neigh_forward_grat enabled. - For regular ARPs/NDs: maintain existing behavior. This allows gratuitous announcements from any input port to be selectively forwarded based on each output port's individual neigh_forward_grat setting, enabling gratuitous neighbor announcements to be flooded to the VXLAN fabric. Regular neighbor discovery (ARP requests, NS queries, solicited replies) remains controlled by neigh_suppress and is unaffected. Reviewed-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Danielle Ratson Acked-by: Nikolay Aleksandrov --- net/bridge/br_arp_nd_proxy.c | 22 ++++++++++++++++++++++ net/bridge/br_forward.c | 15 +++++++++++---- net/bridge/br_private.h | 2 ++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c index 3205346f298c..5263232278b4 100644 --- a/net/bridge/br_arp_nd_proxy.c +++ b/net/bridge/br_arp_nd_proxy.c @@ -132,6 +132,7 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, __be32 sip, tip; BR_INPUT_SKB_CB(skb)->proxyarp_replied = 0; + BR_INPUT_SKB_CB(skb)->grat_arp = 0; if ((dev->flags & IFF_NOARP) || !pskb_may_pull(skb, arp_hdr_len(dev))) @@ -167,6 +168,7 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br, sip == tip) { /* prevent flooding to neigh suppress ports */ BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1; + BR_INPUT_SKB_CB(skb)->grat_arp = 1; return; } } @@ -419,6 +421,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, struct neighbour *n; BR_INPUT_SKB_CB(skb)->proxyarp_replied = 0; + BR_INPUT_SKB_CB(skb)->grat_arp = 0; if (br_is_neigh_suppress_enabled(p, vid)) return; @@ -431,6 +434,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br, !msg->icmph.icmp6_solicited) { /* prevent flooding to neigh suppress ports */ BR_INPUT_SKB_CB(skb)->proxyarp_replied = 1; + BR_INPUT_SKB_CB(skb)->grat_arp = 1; return; } @@ -522,3 +526,21 @@ bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid) return !!(p->flags & BR_NEIGH_SUPPRESS); } } + +bool br_is_neigh_forward_grat_enabled(const struct net_bridge_port *p, u16 vid) +{ + if (!vid) + return !!(p->flags & BR_NEIGH_FORWARD_GRAT); + + if (p->flags & BR_NEIGH_VLAN_SUPPRESS) { + struct net_bridge_vlan_group *vg = nbp_vlan_group_rcu(p); + struct net_bridge_vlan *v; + + v = br_vlan_find(vg, vid); + if (!v) + return false; + return !!(v->priv_flags & BR_VLFLAG_NEIGH_FORWARD_GRAT_ENABLED); + } else { + return !!(p->flags & BR_NEIGH_FORWARD_GRAT); + } +} diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index dea09096ad0f..4a77d0743374 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -230,10 +230,17 @@ void br_flood(struct net_bridge *br, struct sk_buff *skb, /* Do not flood to ports that enable proxy ARP */ if (p->flags & BR_PROXYARP) continue; - if (BR_INPUT_SKB_CB(skb)->proxyarp_replied && - ((p->flags & BR_PROXYARP_WIFI) || - br_is_neigh_suppress_enabled(p, vid))) - continue; + if (BR_INPUT_SKB_CB(skb)->proxyarp_replied) { + if (p->flags & BR_PROXYARP_WIFI) + continue; + /* For gratuitous ARPs/NAs, check neigh_forward_grat. + * For regular ARPs/NDs, check only neigh_suppress. + */ + if (br_is_neigh_suppress_enabled(p, vid) && + (!BR_INPUT_SKB_CB(skb)->grat_arp || + !br_is_neigh_forward_grat_enabled(p, vid))) + continue; + } prev = maybe_deliver(prev, p, skb, local_orig); if (IS_ERR(prev)) { diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 3bc15978a8df..02671e648dac 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -601,6 +601,7 @@ struct br_input_skb_cb { u8 proxyarp_replied:1; u8 src_port_isolated:1; u8 promisc:1; + u8 grat_arp:1; #ifdef CONFIG_BRIDGE_VLAN_FILTERING u8 vlan_filtered:1; #endif @@ -2362,4 +2363,5 @@ 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 *m); 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.51.0