Introduce disable_garp_rarp option in the Logical_Router table in order
to disable GARP/RARP announcements by all the peer ports of this logical
router.
Please note this is a patch specific for ovn branch-25.03.

Reported-at: https://issues.redhat.com/browse/FDP-1537
Signed-off-by: Lorenzo Bianconi <[email protected]>
---
 NEWS                 |  2 +
 controller/pinctrl.c | 45 ++++++++++++++++++---
 northd/northd.c      |  5 +++
 ovn-nb.xml           |  9 +++++
 tests/ovn.at         | 94 ++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 149 insertions(+), 6 deletions(-)

diff --git a/NEWS b/NEWS
index 7dee515c4..57c093d81 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,8 @@ OVN v25.03.3 - xx xxx xxxx
      based L4 field translation for stateful ACLs. When enabled allows proper
      handling of IP fragmentation in userspace datapaths. This option may break
      hardware offloading and is disabled by default.
+   - Added disable_garp_rarp option to logical_router table in order to disable
+     GARP/RARP announcements by all the peer ports of this logical router.
 
 OVN v25.03.2 - 18 Nov 2025
 --------------------------
diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index fa9d0fc9f..27ac1e651 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -5005,6 +5005,8 @@ struct garp_rarp_data {
     uint32_t dp_key;             /* Datapath used to output this GARP. */
     uint32_t port_key;           /* Port to inject the GARP into. */
     char *logical_port;          /* Name of the cr logical_port, if any */
+    bool stale;                  /* Used during sync to remove stale
+                                  * information. */
 };
 
 /* Contains GARPs/RARPs to be sent. Protected by pinctrl_mutex*/
@@ -5077,6 +5079,7 @@ add_garp_rarp(const char *name, const struct eth_addr ea, 
ovs_be32 ip,
     garp_rarp->backoff = 1000; /* msec. */
     garp_rarp->dp_key = dp_key;
     garp_rarp->port_key = port_key;
+    garp_rarp->stale = false;
     garp_rarp->logical_port = nullable_xstrdup(logical_port);
     shash_add(&send_garp_rarp_data, name, garp_rarp);
 
@@ -5120,6 +5123,7 @@ send_garp_rarp_update(struct ovsdb_idl_txn *ovnsb_idl_txn,
                 if (garp_rarp) {
                     garp_rarp->dp_key = binding_rec->datapath->tunnel_key;
                     garp_rarp->port_key = binding_rec->tunnel_key;
+                    garp_rarp->stale = false;
                     if (garp_max_timeout != garp_rarp_max_timeout ||
                         garp_continuous != garp_rarp_continuous) {
                         /* reset backoff */
@@ -5151,6 +5155,7 @@ send_garp_rarp_update(struct ovsdb_idl_txn *ovnsb_idl_txn,
                     if (garp_rarp) {
                         garp_rarp->dp_key = binding_rec->datapath->tunnel_key;
                         garp_rarp->port_key = binding_rec->tunnel_key;
+                        garp_rarp->stale = false;
                         if (garp_max_timeout != garp_rarp_max_timeout ||
                             garp_continuous != garp_rarp_continuous) {
                             /* reset backoff */
@@ -5178,6 +5183,7 @@ send_garp_rarp_update(struct ovsdb_idl_txn *ovnsb_idl_txn,
     if (garp_rarp) {
         garp_rarp->dp_key = binding_rec->datapath->tunnel_key;
         garp_rarp->port_key = binding_rec->tunnel_key;
+        garp_rarp->stale = false;
         if (garp_max_timeout != garp_rarp_max_timeout ||
             garp_continuous != garp_rarp_continuous) {
             /* reset backoff */
@@ -6811,6 +6817,28 @@ send_arp_nd_run(struct rconn *swconn, long long int 
*send_arp_nd_time)
     }
 }
 
+static bool
+garp_rarp_is_enabled(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+                     const struct sbrec_port_binding *pb)
+{
+    if (smap_get_bool(&pb->options, "disable_garp_rarp", false)) {
+        return false;
+    }
+
+    /* Check if GARP probing is disabled on the peer logical router. */
+    const struct sbrec_port_binding *peer = lport_get_peer(
+            pb, sbrec_port_binding_by_name);
+    if (!peer) {
+        peer = lport_get_l3gw_peer(pb, sbrec_port_binding_by_name);
+    }
+    if (peer && smap_get_bool(&peer->datapath->external_ids,
+                              "disable_garp_rarp", false)) {
+        return false;
+    }
+
+    return true;
+}
+
 /* Called by pinctrl_run(). Runs with in the main ovn-controller
  * thread context. */
 static void
@@ -6865,10 +6893,8 @@ send_garp_rarp_prepare(struct ovsdb_idl_txn 
*ovnsb_idl_txn,
      * send_garp_rarp_data. */
     struct shash_node *iter;
     SHASH_FOR_EACH_SAFE (iter, &send_garp_rarp_data) {
-        if (!sset_contains(&localnet_vifs, iter->name) &&
-            !sset_contains(&nat_ip_keys, iter->name)) {
-            send_garp_rarp_delete(iter->name);
-        }
+        struct garp_rarp_data *garp_rarp = iter->data;
+        garp_rarp->stale = true;
     }
 
     /* Update send_garp_rarp_data. */
@@ -6876,7 +6902,7 @@ send_garp_rarp_prepare(struct ovsdb_idl_txn 
*ovnsb_idl_txn,
     SSET_FOR_EACH (iface_id, &localnet_vifs) {
         const struct sbrec_port_binding *pb = lport_lookup_by_name(
             sbrec_port_binding_by_name, iface_id);
-        if (pb && !smap_get_bool(&pb->options, "disable_garp_rarp", false)) {
+        if (pb && garp_rarp_is_enabled(sbrec_port_binding_by_name, pb)) {
             send_garp_rarp_update(ovnsb_idl_txn,
                                   sbrec_mac_binding_by_lport_ip,
                                   local_datapaths, pb, &nat_addresses,
@@ -6889,7 +6915,7 @@ send_garp_rarp_prepare(struct ovsdb_idl_txn 
*ovnsb_idl_txn,
     SSET_FOR_EACH (gw_port, &local_l3gw_ports) {
         const struct sbrec_port_binding *pb
             = lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
-        if (pb && !smap_get_bool(&pb->options, "disable_garp_rarp", false)) {
+        if (pb && garp_rarp_is_enabled(sbrec_port_binding_by_name, pb)) {
             send_garp_rarp_update(ovnsb_idl_txn, sbrec_mac_binding_by_lport_ip,
                                   local_datapaths, pb, &nat_addresses,
                                   garp_max_timeout, garp_continuous);
@@ -6909,6 +6935,13 @@ send_garp_rarp_prepare(struct ovsdb_idl_txn 
*ovnsb_idl_txn,
 
     /* pinctrl_handler thread will send the GARPs. */
 
+    SHASH_FOR_EACH_SAFE (iter, &send_garp_rarp_data) {
+        struct garp_rarp_data *garp_rarp = iter->data;
+        if (garp_rarp->stale) {
+            send_garp_rarp_delete(iter->name);
+        }
+    }
+
     sset_destroy(&localnet_vifs);
     sset_destroy(&local_l3gw_ports);
 
diff --git a/northd/northd.c b/northd/northd.c
index 94c62e064..09af83879 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -837,6 +837,11 @@ ovn_datapath_update_external_ids(struct ovn_datapath *od)
             smap_add_format(&ids, "dynamic-routing-vrf-id", "%"PRId64,
                             vrf_id);
         }
+
+        bool disable_garp_rarp = smap_get_bool(&od->nbr->options,
+                                               "disable_garp_rarp", false);
+        smap_add_format(&ids, "disable_garp_rarp",
+                        disable_garp_rarp ? "true" : "false");
     }
 
     sbrec_datapath_binding_set_external_ids(od->sb, &ids);
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 7c3edcedc..ab7f36fba 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -3290,6 +3290,15 @@ or
         for established sessions as we will commit everything in addition
         to already existing stateful NATs and LBs.
       </column>
+
+      <column name="options" key="disable_garp_rarp"
+              type='{"type": "boolean"}'>
+        <p>
+          If set to <code>true</code>, GARP and RARP announcements are not
+          sent by all the VIF peer ports of this logical router.
+          The default value is <code>false</code>.
+        </p>
+      </column>
     </group>
 
     <group title="Common Columns">
diff --git a/tests/ovn.at b/tests/ovn.at
index ad8391a18..c7f1d49c5 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -43998,3 +43998,97 @@ AT_CHECK([ovs-ofctl dump-flows br-int 
table=$acl_in_eval | grep -q "tp_dst=80"],
 OVN_CLEANUP([hv1])
 AT_CLEANUP
 ])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([Disabling RARP/GARP announcements from Router options])
+AT_SKIP_IF([test $HAVE_SCAPY = no])
+ovn_start
+
+net_add n1
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
+ovn_attach n1 br-phys 192.168.0.1
+
+check ovs-vsctl add-port br-phys snoopvif -- set Interface snoopvif 
options:tx_pcap=hv1/snoopvif-tx.pcap options:rxq_pcap=hv1/snoopvif-rx.pcap
+check ovs-vsctl add-port br-int vif1 -- set Interface vif1 
external-ids:iface-id=lsp1
+check ovs-vsctl add-port br-int vif2 -- set Interface vif2 
external-ids:iface-id=lsp2
+
+check ovs-vsctl set Open_vSwitch . external-ids:garp-max-timeout-sec=1
+
+check ovn-nbctl ls-add ls1
+check ovn-nbctl lsp-add ls1 ln1
+check ovn-nbctl lsp-set-addresses ln1 unknown
+check ovn-nbctl lsp-set-type ln1 localnet
+check ovn-nbctl lsp-set-options ln1 network_name=phys
+check ovn-nbctl lsp-add ls1 lsp1
+check ovn-nbctl lsp-set-addresses lsp1 "00:00:00:00:00:12 192.168.1.2"
+check ovn-nbctl --wait=hv sync
+
+check ovn-nbctl ls-add ls2
+check ovn-nbctl lsp-add ls2 lsp2
+check ovn-nbctl lsp-set-addresses lsp2 "00:00:00:00:00:13 10.0.0.2"
+check ovn-nbctl --wait=hv sync
+
+check ovn-nbctl lr-add lr1
+check ovn-nbctl set Logical_Router lr1 options:disable_garp_rarp="true"
+check ovn-nbctl lrp-add lr1 lrp1 00:00:00:00:00:11 192.168.1.1/24
+check ovn-nbctl lrp-add lr1 lrp2 00:00:00:00:00:14 10.0.0.1/24
+check ovn-nbctl lsp-add ls1 ls-lrp1 \
+    -- set Logical_Switch_Port ls-lrp1 type=router \
+    options:router-port=lrp1 addresses=\"00:00:00:00:00:11\"
+check ovn-nbctl lsp-add ls2 ls-lrp2 \
+    -- set Logical_Switch_Port ls-lrp2 type=router \
+    options:router-port=lrp2 addresses=\"00:00:00:00:00:14\"
+check ovn-nbctl lsp-set-options ls-lrp1 router-port=lrp1 nat-addresses="router"
+check ovn-nbctl lr-nat-add lr1 snat 192.168.1.10 10.0.0.0/24
+check ovn-nbctl lrp-set-gateway-chassis lrp1 hv1
+check ovn-nbctl --wait=hv sync
+
+wait_for_ports_up
+
+garp_lrp=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='00:00:00:00:00:11')/ \
+                    ARP(hwsrc='00:00:00:00:00:11', psrc='192.168.1.1', 
pdst='192.168.1.1')")
+garp_vif=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='00:00:00:00:00:12')/ \
+                    ARP(hwsrc='00:00:00:00:00:12', psrc='192.168.1.2', 
pdst='192.168.1.2')")
+garp_nat=$(fmt_pkt "Ether(dst='ff:ff:ff:ff:ff:ff', src='00:00:00:00:00:11')/ \
+                    ARP(hwsrc='00:00:00:00:00:11', psrc='192.168.1.10', 
pdst='192.168.1.10')")
+# GARP packet for vif
+echo $garp_vif > expected
+OVN_CHECK_PACKETS_UNIQ([hv1/snoopvif-tx.pcap], [expected])
+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap > 
hv1/snoopvif-tx.packets
+AT_CHECK([grep -q "$garp_lrp" hv1/snoopvif-tx.packets], [1])
+AT_CHECK([grep -q "$garp_nat" hv1/snoopvif-tx.packets], [1])
+
+# GARP packet for lrp
+echo $garp_lrp >> expected
+echo $garp_nat >> expected
+check ovn-nbctl --wait=hv set Logical_Router lr1 
options:disable_garp_rarp="false"
+OVN_CHECK_PACKETS_UNIQ([hv1/snoopvif-tx.pcap], [expected])
+
+# Check for GW router
+check ovn-nbctl lrp-del-gateway-chassis lrp1 hv1
+check ovn-nbctl set Logical_Router lr1 options:chassis="hv1"
+check ovn-nbctl set Logical_Router lr1 options:disable_garp_rarp="true"
+check ovn-nbctl --wait=hv sync
+
+sleep_controller hv1
+reset_pcap_file snoopvif hv1/snoopvif
+wake_up_controller hv1
+
+echo $garp_vif > expected
+OVN_CHECK_PACKETS_UNIQ([hv1/snoopvif-tx.pcap], [expected])
+$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap > 
hv1/snoopvif-tx.packets
+AT_CHECK([grep -q "$garp_lrp" hv1/snoopvif-tx.packets], [1])
+AT_CHECK([grep -q "$garp_nat" hv1/snoopvif-tx.packets], [1])
+
+echo $garp_lrp >> expected
+echo $garp_nat >> expected
+check ovn-nbctl set Logical_Router lr1 options:disable_garp_rarp="false"
+check ovn-nbctl --wait=hv sync
+OVN_CHECK_PACKETS_UNIQ([hv1/snoopvif-tx.pcap], [expected])
+
+AT_CLEANUP
+])
-- 
2.53.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to