diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index a06cae3cc..8691c3076 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -531,6 +531,21 @@ update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl,
     }
 }
 
+static void
+add_pending_ct_zone_entry(struct shash *pending_ct_zones,
+                          enum ct_zone_pending_state state,
+                          int zone, bool add, char *name)
+{
+    VLOG_DBG("%s ct zone %"PRId32" for '%s'",
+             add ? "assigning" : "removing", zone, name);
+
+    struct ct_zone_pending_entry *pending = xmalloc(sizeof *pending);
+    pending->state = state; /* Skip flushing zone. */
+    pending->zone = zone;
+    pending->add = add;
+    shash_add(pending_ct_zones, name, pending);
+}
+
 static void
 update_ct_zones(const struct sset *lports, const struct hmap *local_datapaths,
                 struct simap *ct_zones, unsigned long *ct_zone_bitmap,
@@ -540,6 +555,7 @@ update_ct_zones(const struct sset *lports, const struct hmap *local_datapaths,
     int scan_start = 1;
     const char *user;
     struct sset all_users = SSET_INITIALIZER(&all_users);
+    struct simap req_snat_zones = SIMAP_INITIALIZER(&req_snat_zones);
 
     SSET_FOR_EACH(user, lports) {
         sset_add(&all_users, user);
@@ -554,6 +570,25 @@ update_ct_zones(const struct sset *lports, const struct hmap *local_datapaths,
         char *snat = alloc_nat_zone_key(&ld->datapath->header_.uuid, "snat");
         sset_add(&all_users, dnat);
         sset_add(&all_users, snat);
+
+        int req_snat_zone = datapath_snat_ct_zone(ld->datapath);
+        if (req_snat_zone >= 0) {
+            struct simap_node *node;
+            bool collision = false;
+            SIMAP_FOR_EACH (node, &req_snat_zones) {
+                if (node->data == req_snat_zone) {
+                    VLOG_WARN("Datapaths %.*s and " UUID_FMT " request SNAT "
+                              "CT zone %d\n", UUID_LEN, node->name,
+                              UUID_ARGS(&ld->datapath->header_.uuid),
+                              req_snat_zone);
+                    collision = true;
+                    break;
+                }
+            }
+            if (!collision) {
+                simap_put(&req_snat_zones, snat, req_snat_zone);
+            }
+        }
         free(dnat);
         free(snat);
     }
@@ -564,17 +599,51 @@ update_ct_zones(const struct sset *lports, const struct hmap *local_datapaths,
             VLOG_DBG("removing ct zone %"PRId32" for '%s'",
                      ct_zone->data, ct_zone->name);
 
-            struct ct_zone_pending_entry *pending = xmalloc(sizeof *pending);
-            pending->state = CT_ZONE_DB_QUEUED; /* Skip flushing zone. */
-            pending->zone = ct_zone->data;
-            pending->add = false;
-            shash_add(pending_ct_zones, ct_zone->name, pending);
+            add_pending_ct_zone_entry(pending_ct_zones, CT_ZONE_DB_QUEUED,
+                                      ct_zone->data, false, ct_zone->name);
 
             bitmap_set0(ct_zone_bitmap, ct_zone->data);
             simap_delete(ct_zones, ct_zone);
         }
     }
 
+    /* Prioritize requested CT zones */
+    struct simap_node *snat_req_node;
+    SIMAP_FOR_EACH (snat_req_node, &req_snat_zones) {
+        struct simap_node *node = simap_find(ct_zones, snat_req_node->name);
+        if (node) {
+            if (node->data == snat_req_node->data) {
+                /* Already have this zone reserved */
+                continue;
+            } else {
+                /* Zone has changed for this node. delete old entry */
+                bitmap_set0(ct_zone_bitmap, node->data);
+                simap_delete(ct_zones, node);
+            }
+        } else if (snat_req_node->data > 0 &&
+                   bitmap_is_set(ct_zone_bitmap, snat_req_node->data)) {
+            /* Uh oh. Someone else already has this zone assigned.
+             * We need to find who and remove them from ct_zones so
+             * that they get re-assigned a new zone below
+             */
+            struct simap_node *next;
+            SIMAP_FOR_EACH_SAFE(node, next, ct_zones) {
+                if (node->data == snat_req_node->data) {
+                    simap_delete(ct_zones, node);
+                    break;
+                }
+            }
+        }
+
+        add_pending_ct_zone_entry(pending_ct_zones, CT_ZONE_OF_QUEUED,
+                                  snat_req_node->data, true,
+                                  snat_req_node->name);
+
+        bitmap_set1(ct_zone_bitmap, snat_req_node->data);
+        simap_put(ct_zones, snat_req_node->name, snat_req_node->data);
+    }
+    simap_destroy(&req_snat_zones);
+
     /* xxx This is wasteful to assign a zone to each port--even if no
      * xxx security policy is applied. */
 
@@ -596,13 +665,8 @@ update_ct_zones(const struct sset *lports, const struct hmap *local_datapaths,
         }
         scan_start = zone + 1;
 
-        VLOG_DBG("assigning ct zone %"PRId32" to '%s'", zone, user);
-
-        struct ct_zone_pending_entry *pending = xmalloc(sizeof *pending);
-        pending->state = CT_ZONE_OF_QUEUED;
-        pending->zone = zone;
-        pending->add = true;
-        shash_add(pending_ct_zones, user, pending);
+        add_pending_ct_zone_entry(pending_ct_zones, CT_ZONE_OF_QUEUED,
+                                  zone, true, user);
 
         bitmap_set1(ct_zone_bitmap, zone);
         simap_put(ct_zones, user, zone);
@@ -2330,6 +2394,7 @@ main(int argc, char *argv[])
 
     engine_add_input(&en_ct_zones, &en_ovs_open_vswitch, NULL);
     engine_add_input(&en_ct_zones, &en_ovs_bridge, NULL);
+    engine_add_input(&en_ct_zones, &en_sb_datapath_binding, NULL);
     engine_add_input(&en_ct_zones, &en_runtime_data, NULL);
 
     engine_add_input(&en_runtime_data, &en_ofctrl_is_connected, NULL);
diff --git a/controller/physical.c b/controller/physical.c
index 1bc2c389b..00c4ca4fd 100644
--- a/controller/physical.c
+++ b/controller/physical.c
@@ -926,7 +926,7 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
 
         ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100,
                         binding->header_.uuid.parts[0],
-                        &match, ofpacts_p, &binding->header_.uuid);
+                        &match, ofpacts_p, hc_uuid);
         return;
     }
 
diff --git a/lib/ovn-util.c b/lib/ovn-util.c
index abe6b04a7..0a6758ab1 100644
--- a/lib/ovn-util.c
+++ b/lib/ovn-util.c
@@ -532,6 +532,13 @@ datapath_is_switch(const struct sbrec_datapath_binding *ldp)
 {
     return smap_get(&ldp->external_ids, "logical-switch") != NULL;
 }
+
+int
+datapath_snat_ct_zone(const struct sbrec_datapath_binding *dp)
+{
+    return smap_get_int(&dp->external_ids, "snat-ct-zone", -1);
+}
+
 
 struct tnlid_node {
     struct hmap_node hmap_node;
diff --git a/lib/ovn-util.h b/lib/ovn-util.h
index a39cbef5a..a035c86e1 100644
--- a/lib/ovn-util.h
+++ b/lib/ovn-util.h
@@ -107,6 +107,7 @@ uint32_t ovn_logical_flow_hash(const struct uuid *logical_datapath,
                                uint16_t priority,
                                const char *match, const char *actions);
 bool datapath_is_switch(const struct sbrec_datapath_binding *);
+int datapath_snat_ct_zone(const struct sbrec_datapath_binding *ldp);
 void ovn_conn_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
                    const char *argv[] OVS_UNUSED, void *idl_);
 
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index 684c2bd47..7b22f2c3e 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -1179,6 +1179,16 @@ ovn_datapath_update_external_ids(struct ovn_datapath *od)
             smap_add(&ids, "interconn-ts", ts);
         }
     }
+
+    /* Set snat-ct-zone */
+    if (od->nbr) {
+        int nat_default_ct = smap_get_int(&od->nbr->options,
+                                           "snat-ct-zone", -1);
+        if (nat_default_ct >= 0) {
+            smap_add_format(&ids, "snat-ct-zone", "%d", nat_default_ct);
+        }
+    }
+
     sbrec_datapath_binding_set_external_ids(od->sb, &ids);
     smap_destroy(&ids);
 }
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 5e8635992..1be1ca6c0 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -1941,6 +1941,13 @@
         unique key for each datapath by itself.  However, if it is configured,
         <code>ovn-northd</code> honors the configured value.
       </column>
+      <column name="options" key="snat-ct-zone"
+          type='{"type": "integer", "minInteger": 0, "maxInteger": 65535}'>
+        Use the requested conntrack zone for SNAT with this router. This can be
+        useful if egress traffic from the host running OVN comes from both OVN and
+        other sources. This way, OVN and the other sources can make use of the same
+        conntrack zone.
+      </column>
     </group>
 
     <group title="Common Columns">
diff --git a/tests/ovn.at b/tests/ovn.at
index f154e3d77..0e902b49e 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -22578,3 +22578,94 @@ AT_CHECK([test "$encap_rec_mvtep" == "$encap_rec_mvtep1"], [0], [])
 
 OVN_CLEANUP([hv1])
 AT_CLEANUP
+
+AT_SETUP([ovn -- snat default ct zone])
+ovn_start
+
+net_add n1
+sim_add hv1
+ovs-vsctl add-br br-phys
+as hv1
+ovn_attach n1 br-phys 192.168.0.10
+
+ovn-nbctl ls-add sw0
+ovn-nbctl lsp-add sw0 sw0-p1
+ovn-nbctl lsp-set-addresses sw0-p1 "00:00:00:00:00:02 10.0.0.2"
+
+ovn-nbctl lr-add gw_router
+ovn-nbctl set Logical_Router gw_router options:chassis="hv1"
+
+ovn-nbctl lrp-add gw_router gw_router-sw0 00:00:00:00:00:01 10.0.0.1/24
+ovn-nbctl lsp-add sw0 sw0-gw_router
+ovn-nbctl lsp-set-addresses sw0-gw_router router
+ovn-nbctl set Logical_Switch_Port sw0-gw_router type=router \
+                             options:router-port=gw_router-sw0 \
+
+ovn-nbctl lr-nat-add gw_router snat 192.168.0.1 10.0.0.0/24
+
+as hv1 ovs-vsctl -- add-port br-int hv1-vif1 -- \
+    set interface hv1-vif1 external-ids:iface-id=sw0-p1
+
+ovn-nbctl --wait=hv sync
+
+ro_nb_uuid=$(ovn-nbctl get Logical_Router gw_router _uuid)
+sw_nb_uuid=$(ovn-nbctl get Logical_Switch sw0 _uuid)
+ro_sb_uuid=$(ovn-sbctl --bare --columns=_uuid find Datapath_Binding external-ids:logical-router=${ro_nb_uuid})
+sw_sb_uuid=$(ovn-sbctl --bare --columns=_uuid find Datapath_Binding external-ids:logical-switch=${sw_nb_uuid})
+ro_meta=$(ovn-sbctl get Datapath_Binding ${ro_sb_uuid} tunnel_key)
+ro_meta=$(printf %#x ${ro_meta})
+sw_meta=$(ovn-sbctl get Datapath_Binding ${sw_sb_uuid} tunnel_key)
+sw_meta=$(printf %#x ${sw_meta})
+
+echo "ro_nb_uuid: ${ro_nb_uuid}"
+echo "sw_nb_uuid: ${sw_nb_uuid}"
+echo "ro_sb_uuid: ${ro_sb_uuid}"
+echo "sw_sb_uuid: ${sw_sb_uuid}"
+echo "ro_meta: ${ro_meta}"
+echo "sw_meta: ${sw_meta}"
+
+as hv1
+ovs-vsctl list bridge br-int
+snat_zone=$(printf %#x $(ovs-vsctl get bridge br-int external-ids:ct-zone-${ro_sb_uuid}_snat | tr -d \"))
+
+echo "snat_zone: ${snat_zone}"
+
+as hv1 ovs-ofctl dump-flows br-int > offlows_pre
+AT_CAPTURE_FILE([offlows_pre])
+# We should have a flow in table 33 that transitions from the ingress pipeline
+# to the egress pipeline of gw_router.
+AT_CHECK_UNQUOTED([grep -c "table=33.*metadata=${ro_meta}.*load:${snat_zone}->NXM_NX_REG12[]" offlows_pre], [0], [dnl
+1
+])
+
+# We should have a flow in table 65 that transitions from the egress pipeline
+# of sw0 to the ingress pipeline of gw_router.
+AT_CHECK_UNQUOTED([grep -c "table=65.*metadata=${sw_meta}.*load:${snat_zone}->NXM_NX_REG12[]" offlows_pre], [0], [dnl
+1
+])
+
+ovn-nbctl --wait=hv set Logical_Router gw_router options:snat-ct-zone=666
+
+as hv1
+snat_zone=$(ovs-vsctl get bridge br-int external-ids:ct-zone-${ro_sb_uuid}_snat | tr -d \")
+
+echo "snat_zone: ${snat_zone}"
+
+AT_CHECK([test "${snat_zone}" = "666"], [0], [])
+
+as hv1 ovs-ofctl dump-flows br-int > offlows_post
+AT_CAPTURE_FILE([offlows_post])
+# We should have a flow in table 33 that transitions from the ingress pipeline
+# to the egress pipeline of gw_router.
+AT_CHECK_UNQUOTED([grep -c "table=33.*metadata=${ro_meta}.*load:0x29a->NXM_NX_REG12[]" offlows_post], [0], [dnl
+1
+])
+
+# We should have a flow in table 65 that transitions from the egress pipeline
+# of sw0 to the ingress pipeline of gw_router.
+AT_CHECK_UNQUOTED([grep -c "table=65.*metadata=${sw_meta}.*load:0x29a->NXM_NX_REG12[]" offlows_post], [0], [dnl
+1
+])
+
+OVN_CLEANUP([hv1])
+AT_CLEANUP
