diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 02e6ef5..da3e83a 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -658,7 +658,8 @@ main(int argc, char *argv[])
             enum mf_field_id mff_ovn_geneve = ofctrl_run(br_int,
                                                          &pending_ct_zones);
 
-            pinctrl_run(&ctx, &lports, br_int, chassis, &local_datapaths);
+            pinctrl_run(&ctx, &lports, br_int, chassis, &chassis_index,
+                        &local_datapaths, &active_tunnels);
             update_ct_zones(&local_lports, &local_datapaths, &ct_zones,
                             ct_zone_bitmap, &pending_ct_zones);
             if (ctx.ovs_idl_txn) {
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index 660233a..b7bcee6 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -23,6 +23,7 @@
 #include "dirs.h"
 #include "dp-packet.h"
 #include "flow.h"
+#include "gchassis.h"
 #include "lport.h"
 #include "nx-match.h"
 #include "ovn-controller.h"
@@ -72,7 +73,9 @@ static void send_garp_wait(void);
 static void send_garp_run(const struct ovsrec_bridge *,
                           const struct sbrec_chassis *,
                           const struct lport_index *lports,
-                          struct hmap *local_datapaths);
+                          const struct chassis_index *chassis_index,
+                          struct hmap *local_datapaths,
+                          struct sset *active_tunnels);
 static void pinctrl_handle_nd_na(const struct flow *ip_flow,
                                  const struct match *md,
                                  struct ofpbuf *userdata);
@@ -1016,7 +1019,9 @@ void
 pinctrl_run(struct controller_ctx *ctx, const struct lport_index *lports,
             const struct ovsrec_bridge *br_int,
             const struct sbrec_chassis *chassis,
-            struct hmap *local_datapaths)
+            const struct chassis_index *chassis_index,
+            struct hmap *local_datapaths,
+            struct sset *active_tunnels)
 {
     char *target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int->name);
     if (strcmp(target, rconn_get_target(swconn))) {
@@ -1051,7 +1056,8 @@ pinctrl_run(struct controller_ctx *ctx, const struct lport_index *lports,
     }
 
     run_put_mac_bindings(ctx, lports);
-    send_garp_run(br_int, chassis, lports, local_datapaths);
+    send_garp_run(br_int, chassis, lports, chassis_index, local_datapaths,
+                  active_tunnels);
 }
 
 void
@@ -1490,11 +1496,26 @@ get_localnet_vifs_l3gwports(const struct ovsrec_bridge *br_int,
 static bool
 pinctrl_is_chassis_resident(const struct lport_index *lports,
                             const struct sbrec_chassis *chassis,
+                            const struct chassis_index *chassis_index,
+                            struct sset *active_tunnels,
                             const char *port_name)
 {
     const struct sbrec_port_binding *pb
         = lport_lookup_by_name(lports, port_name);
-    return pb && pb->chassis && pb->chassis == chassis;
+    if (!pb || !pb->chassis) {
+        return false;
+    }
+    if (strcmp(pb->type, "chassisredirect")) {
+        return pb->chassis == chassis;
+    } else {
+        struct ovs_list *gateway_chassis =
+            gateway_chassis_get_ordered(pb, chassis_index);
+        bool active = gateway_chassis_is_active(gateway_chassis,
+                                                chassis,
+                                                active_tunnels);
+        gateway_chassis_destroy(gateway_chassis);
+        return active;
+    }
 }
 
 /* Extracts the mac, IPv4 and IPv6 addresses, and logical port from
@@ -1569,13 +1590,16 @@ consider_nat_address(const char *nat_address,
                      struct sset *nat_address_keys,
                      const struct lport_index *lports,
                      const struct sbrec_chassis *chassis,
+                     const struct chassis_index *chassis_index,
+                     struct sset *active_tunnels,
                      struct shash *nat_addresses)
 {
     struct lport_addresses *laddrs = xmalloc(sizeof *laddrs);
     char *lport = NULL;
     if (!extract_addresses_with_port(nat_address, laddrs, &lport)
         || (!lport && !strcmp(pb->type, "patch"))
-        || (lport && !pinctrl_is_chassis_resident(lports, chassis, lport))) {
+        || (lport && !pinctrl_is_chassis_resident(
+            lports, chassis, chassis_index, active_tunnels, lport))) {
         destroy_lport_addresses(laddrs);
         free(laddrs);
         free(lport);
@@ -1598,6 +1622,8 @@ get_nat_addresses_and_keys(struct sset *nat_address_keys,
                            struct sset *local_l3gw_ports,
                            const struct lport_index *lports,
                            const struct sbrec_chassis *chassis,
+                           const struct chassis_index *chassis_index,
+                           struct sset *active_tunnels,
                            struct shash *nat_addresses)
 {
     const char *gw_port;
@@ -1612,6 +1638,7 @@ get_nat_addresses_and_keys(struct sset *nat_address_keys,
             for (int i = 0; i < pb->n_nat_addresses; i++) {
                 consider_nat_address(pb->nat_addresses[i], pb,
                                      nat_address_keys, lports, chassis,
+                                     chassis_index, active_tunnels,
                                      nat_addresses);
             }
         } else {
@@ -1622,6 +1649,7 @@ get_nat_addresses_and_keys(struct sset *nat_address_keys,
             if (nat_addresses_options) {
                 consider_nat_address(nat_addresses_options, pb,
                                      nat_address_keys, lports, chassis,
+                                     chassis_index, active_tunnels,
                                      nat_addresses);
             }
         }
@@ -1638,7 +1666,9 @@ static void
 send_garp_run(const struct ovsrec_bridge *br_int,
               const struct sbrec_chassis *chassis,
               const struct lport_index *lports,
-              struct hmap *local_datapaths)
+              const struct chassis_index *chassis_index,
+              struct hmap *local_datapaths,
+              struct sset *active_tunnels)
 {
     struct sset localnet_vifs = SSET_INITIALIZER(&localnet_vifs);
     struct sset local_l3gw_ports = SSET_INITIALIZER(&local_l3gw_ports);
@@ -1652,7 +1682,8 @@ send_garp_run(const struct ovsrec_bridge *br_int,
                       &localnet_vifs, &localnet_ofports, &local_l3gw_ports);
 
     get_nat_addresses_and_keys(&nat_ip_keys, &local_l3gw_ports, lports,
-                               chassis, &nat_addresses);
+                               chassis, chassis_index, active_tunnels,
+                               &nat_addresses);
     /* For deleted ports and deleted nat ips, remove from send_garp_data. */
     struct shash_node *iter, *next;
     SHASH_FOR_EACH_SAFE (iter, next, &send_garp_data) {
diff --git a/ovn/controller/pinctrl.h b/ovn/controller/pinctrl.h
index 230580b..913c170 100644
--- a/ovn/controller/pinctrl.h
+++ b/ovn/controller/pinctrl.h
@@ -19,8 +19,10 @@
 
 #include <stdint.h>
 
+#include "lib/sset.h"
 #include "openvswitch/meta-flow.h"
 
+struct chassis_index;
 struct controller_ctx;
 struct hmap;
 struct lport_index;
@@ -30,7 +32,8 @@ struct sbrec_chassis;
 void pinctrl_init(void);
 void pinctrl_run(struct controller_ctx *, const struct lport_index *,
                  const struct ovsrec_bridge *, const struct sbrec_chassis *,
-                 struct hmap *local_datapaths);
+                 const struct chassis_index *, struct hmap *local_datapaths,
+                 struct sset *active_tunnels);
 void pinctrl_wait(struct controller_ctx *);
 void pinctrl_destroy(void);
 
diff --git a/tests/ovn.at b/tests/ovn.at
index 5f10d52..5a0b761 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -7959,3 +7959,124 @@ AT_CHECK([ovn-sbctl --columns chassis --bare find Port_Binding logical_port=cr-o
 OVN_CLEANUP([gw1],[gw2],[hv1],[hv2])
 
 AT_CLEANUP
+
+AT_SETUP([ovn -- send gratuitous ARP for NAT rules on HA distributed router])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+ovn-nbctl ls-add ls0
+ovn-nbctl ls-add ls1
+ovn-nbctl create Logical_Router name=lr0
+ovn-nbctl lrp-add lr0 lrp0 f0:00:00:00:00:01 192.168.0.100/24
+
+ovn-nbctl --id=@gc0 create Gateway_Chassis \
+                    name=outside_gw1 chassis_name=hv2 priority=10 -- \
+          --id=@gc1 create Gateway_Chassis \
+                    name=outside_gw2 chassis_name=hv3 priority=1 -- \
+          set Logical_Router_Port lrp0 'gateway_chassis=[@gc0,@gc1]'
+
+ovn-nbctl lsp-add ls0 lrp0-rp -- set Logical_Switch_Port lrp0-rp \
+    type=router options:router-port=lrp0 addresses="router"
+ovn-nbctl lrp-add lr0 lrp1 f0:00:00:00:00:02 10.0.0.1/24
+ovn-nbctl lsp-add ls1 lrp1-rp -- set Logical_Switch_Port lrp1-rp \
+    type=router options:router-port=lrp1 addresses="router"
+
+# Add NAT rules
+AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 192.168.0.100 10.0.0.0/24])
+
+net_add n1
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
+AT_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])
+
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+AT_CHECK([as hv2 ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
+
+sim_add hv3
+as hv3
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.3
+AT_CHECK([as hv3 ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
+
+# Create a localnet port.
+AT_CHECK([ovn-nbctl lsp-add ls0 ln_port])
+AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown])
+AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet])
+AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1])
+
+# wait for earlier changes to take effect
+AT_CHECK([ovn-nbctl --timeout=3 --wait=sb sync], [0], [ignore])
+
+reset_pcap_file() {
+    local iface=$1
+    local pcap_file=$2
+    ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
+options:rxq_pcap=dummy-rx.pcap
+    rm -f ${pcap_file}*.pcap
+    ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
+options:rxq_pcap=${pcap_file}-rx.pcap
+}
+
+as hv1 reset_pcap_file snoopvif hv1/snoopvif
+as hv2 reset_pcap_file br-phys_n1 hv2/br-phys_n1
+as hv3 reset_pcap_file br-phys_n1 hv3/br-phys_n1
+# add nat-addresses option
+ovn-nbctl --wait=sb lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router"
+
+# Wait for packets to be received through hv2.
+OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 100])
+trim_zeros() {
+    sed 's/\(00\)\{1,\}$//'
+}
+
+garp="fffffffffffff0000000000108060001080006040001f00000000001c0a80064000000000000c0a80064"
+echo $garp >> expout
+echo $garp >> expout
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros > hv1_snoop_tx
+echo "packets on hv1-snoopvif:"
+cat hv1_snoop_tx
+AT_CHECK([sort hv1_snoop_tx], [0], [expout])
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-phys_n1-tx.pcap | trim_zeros > hv2_br_phys_tx
+echo "packets on hv2 br-phys tx"
+cat hv2_br_phys_tx
+AT_CHECK([grep $garp hv2_br_phys_tx | sort], [0], [expout])
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv3/br-phys_n1-tx.pcap | trim_zeros > hv3_br_phys_tx
+echo "packets on hv3 br-phys tx"
+cat hv3_br_phys_tx
+AT_CHECK([grep $garp hv3_br_phys_tx | sort], [0], [])
+
+
+# at this point, we invert the priority of the gw chassis between hv2 and hv3
+
+ovn-nbctl --wait=hv \
+          --id=@gc0 create Gateway_Chassis \
+                    name=outside_gw1 chassis_name=hv2 priority=1 -- \
+          --id=@gc1 create Gateway_Chassis \
+                    name=outside_gw2 chassis_name=hv3 priority=10 -- \
+          set Logical_Router_Port lrp0 'gateway_chassis=[@gc0,@gc1]'
+
+
+as hv1 reset_pcap_file snoopvif hv1/snoopvif
+as hv2 reset_pcap_file br-phys_n1 hv2/br-phys_n1
+as hv3 reset_pcap_file br-phys_n1 hv3/br-phys_n1
+
+# Wait for packets to be received.
+OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 100])
+trim_zeros() {
+    sed 's/\(00\)\{1,\}$//'
+}
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros >  hv1_snoopvif_tx
+AT_CHECK([sort hv1_snoopvif_tx], [0], [expout])
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv3/br-phys_n1-tx.pcap | trim_zeros > hv3_br_phys_tx
+AT_CHECK([grep $garp hv3_br_phys_tx | sort], [0], [expout])
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-phys_n1-tx.pcap | trim_zeros > hv2_br_phys_tx
+AT_CHECK([grep $garp hv2_br_phys_tx | sort], [0], [])
+OVN_CLEANUP([hv1],[hv2],[hv3])
+
+AT_CLEANUP
