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.

Signed-off-by: Danielle Ratson <[email protected]>
Reviewed-by: Ido Schimmel <[email protected]>
Reviewed-by: Petr Machata <[email protected]>
---
 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 677cd5d68dc7..377fd0933409 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -605,6 +605,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
@@ -2366,4 +2367,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


Reply via email to