diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index 4783e43d7..ea7db3d47 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -11304,6 +11304,7 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath *od,
         ovs_be32 ip, mask;
         struct in6_addr ipv6, mask_v6, v6_exact = IN6ADDR_EXACT_INIT;
         bool is_v6 = false;
+        bool is_masquerade = false;
         bool stateless = lrouter_nat_is_stateless(nat);
         struct nbrec_address_set *allowed_ext_ips =
                                   nat->allowed_ext_ips;
@@ -11343,9 +11344,15 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath *od,
         if (is_v6) {
             error = ipv6_parse_masked(nat->logical_ip, &ipv6, &mask_v6);
             cidr_bits = ipv6_count_cidr_bits(&mask_v6);
+            if (cidr_bits < 128) {
+                is_masquerade = true;
+            }
         } else {
             error = ip_parse_masked(nat->logical_ip, &ip, &mask);
             cidr_bits = ip_count_cidr_bits(mask);
+            if (cidr_bits < 32) {
+                is_masquerade = 32;
+            }
         }
         if (!strcmp(nat->type, "snat")) {
             if (error) {
@@ -11396,6 +11403,56 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath *od,
         build_lrouter_in_dnat_flow(lflows, od, nat, match, actions, distributed,
                                    mask, is_v6);
 
+        /* When router have SNAT enabled, and logical_ip is a network (router
+         * is doing masquerade), then we need to make sure that packets
+         * unrelated to any established connection that still have router's
+         * external IP as a next hop after going through lr_in_unsnat table
+         * are dropped properly. Otherwise such packets will loop around
+         * between tables until their ttl reaches zero - this additionally
+         * causes kernel module to drop such packages due to recirculation
+         * limit being exceeded.
+         *
+         * Install a logical flow in lr_in_ip_routing table that will
+         * match packet with router's external IP that have no related
+         * conntrack entries and drop them. Flow priority must be higher
+         * than any other flow in lr_in_ip_routing that matches router's
+         * external IP.
+         *
+         * The priority for destination routes is calculated as
+         * (prefix length * 2) + 1, and there is an additional flow
+         * for when BFD is in play with priority + 1. Set priority that
+         * is higher than any other potential routing flow for that
+         * network, that is (prefix length * 2) + offset, where offset
+         * is 1 (dst) + 1 (bfd) + 1. */
+        if (is_masquerade) {
+            uint16_t priority, prefix_length, offset;
+
+            if (is_v6) {
+                prefix_length = 128;
+            } else {
+                prefix_length = 32;
+            }
+            offset = 3;
+            priority = (prefix_length * 2) + offset;
+
+            ds_clear(match);
+
+            if (is_v6) {
+                ds_put_format(match,
+                              "ct.new && ip6.dst == %s",
+                              nat->external_ip);
+            } else {
+                ds_put_format(match,
+                              "ct.new && ip4.dst == %s",
+                              nat->external_ip);
+            }
+
+            ovn_lflow_add_unique_with_hint(lflows, od,
+                                           S_ROUTER_IN_IP_ROUTING, priority,
+                                           ds_cstr(match), "drop;",
+                                           &nat->header_);
+        }
+
         /* ARP resolve for NAT IPs. */
         if (od->l3dgw_port) {
             if (!strcmp(nat->type, "snat")) {
diff --git a/tests/ovn.at b/tests/ovn.at
index b751d6db2..b40eaeaf6 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -23220,6 +23220,51 @@ OVN_CLEANUP([hv1])
 AT_CLEANUP
 ])
 
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn -- SNAT handling of unrelated packets with masquerade"])
+ovn_start
+
+ovn-nbctl lr-add R1
+
+ovn-nbctl ls-add sw0
+ovn-nbctl ls-add public
+
+ovn-nbctl lsp-add sw0 sw0-port1 \
+          -- lsp-set-addresses sw0-port1 "00:00:00:01:02:03 192.168.1.3"
+
+ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24
+ovn-nbctl lrp-add R1 rp-public 00:00:02:01:02:03 172.16.1.254/24 \
+    -- lrp-set-gateway-chassis rp-public hv1
+
+ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \
+    type=router options:router-port=rp-sw0 \
+    -- lsp-set-addresses sw0-rp router
+
+ovn-nbctl lsp-add public public-rp -- set Logical_Switch_Port public-rp \
+    type=router options:router-port=rp-public \
+    -- lsp-set-addresses public-rp router
+
+AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.2 192.168.1.3 sw0-port1 00:00:00:01:02:03])
+AT_CHECK([ovn-nbctl lr-nat-add R1 dnat 172.16.1.3 192.168.1.4])
+AT_CHECK([check ovn-nbctl --wait=sb sync])
+
+# Before we add second NAT (masquerade) there should be no flows
+# in lr_in_ip_routing that match against conntrack state.
+AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_routing.*ct.new"], [1], [])
+
+ovn-nbctl lr-nat-add R1 snat 172.16.1.254 192.168.1.0/24
+AT_CHECK([check ovn-nbctl --wait=sb sync])
+
+AT_CHECK([ovn-sbctl lflow-list | grep -E "lr_in_ip_routing.*ct.new.*drop"],
+[0], [dnl
+  table=10(lr_in_ip_routing   ), priority=67   dnl
+, match=(ct.new && ip4.dst == 172.16.1.254), dnl
+action=(drop;)
+])
+
+AT_CLEANUP
+])
+
 OVN_FOR_EACH_NORTHD([
 AT_SETUP([ovn -- ARP replies for SNAT external ips])
 ovn_start
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index 4885303d1..3edd993cd 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -5600,6 +5600,114 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
 AT_CLEANUP
 ])
 
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn -- SNAT handling of unrelated packets with masquerade"])
+AT_SKIP_IF([test $HAVE_NC = no])
+
+ovn_start
+OVS_TRAFFIC_VSWITCHD_START()
+
+ADD_BR([br-int])
+ADD_BR([br-ext])
+
+ovs-ofctl add-flow br-ext action=normal
+# Set external-ids in br-int needed for ovn-controller
+ovs-vsctl \
+        -- set Open_vSwitch . external-ids:system-id=hv1 \
+        -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
+        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
+        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
+        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
+
+# Start ovn-controller
+start_daemon ovn-controller
+
+ovn-nbctl lr-add R1
+
+ovn-nbctl ls-add sw0
+ovn-nbctl ls-add public
+
+ovn-nbctl lrp-add R1 rp-sw0 00:00:01:01:02:03 192.168.1.1/24
+ovn-nbctl lrp-add R1 rp-public 00:00:02:01:02:03 172.16.1.254/24 \
+    -- lrp-set-gateway-chassis rp-public hv1
+
+ovn-nbctl lsp-add sw0 sw0-rp -- set Logical_Switch_Port sw0-rp \
+    type=router options:router-port=rp-sw0 \
+    -- lsp-set-addresses sw0-rp router
+
+ovn-nbctl lsp-add public public-rp -- set Logical_Switch_Port public-rp \
+    type=router options:router-port=rp-public \
+    -- lsp-set-addresses public-rp router
+
+ovn-nbctl lr-nat-add R1 snat 172.16.1.254 192.168.1.0/24
+
+ADD_NAMESPACES(sw01-x)
+ADD_VETH(sw01-x, sw01-x, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
+         "192.168.1.1")
+ovn-nbctl lsp-add sw0 sw01-x \
+    -- lsp-set-addresses sw01-x "f0:00:00:01:02:03 192.168.1.2"
+
+OVS_WAIT_UNTIL([test x$(ovn-nbctl lsp-get-up sw01-x) = xup])
+
+ADD_NAMESPACES(ext-foo)
+ADD_VETH(ext-foo, ext-foo, br-ext, "172.16.1.100/24", "00:10:10:01:02:13", \
+         "172.16.1.254")
+
+OVS_WAIT_UNTIL([test "$(ip netns exec ext-foo ip a | grep 172.16 | grep tentative)" = ""])
+
+AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext])
+ovn-nbctl lsp-add public public1 \
+        -- lsp-set-addresses public1 unknown \
+        -- lsp-set-type public1 localnet \
+        -- lsp-set-options public1 network_name=phynet
+
+ovn-nbctl --wait=hv sync
+
+# Send ping from ext-foo to router's external IP to verify that incoming ICMP is working
+NS_CHECK_EXEC([ext-foo], [ping -q -c 3 -i 0.3 -w 2 172.16.1.254 | FORMAT_PING], \
+[0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+
+# Verify that we can establish an outgoing connection from host behind
+# SNAT to one on the external network.
+NS_CHECK_EXEC([ext-foo], [echo response | nc -l 80 &], [], [])
+
+NS_CHECK_EXEC([sw01-x], [nc -w 1 172.16.1.100 80], [0], [dnl
+response
+])
+
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
+
+# Check that incoming packets that are not part of any established
+# connection are dropped.
+NS_CHECK_EXEC([ext-foo], [nc -w 1 172.16.1.254 80], [1], [])
+
+# There shouldn't be any entry in conntrack table for that flow.
+AT_CHECK([ovs-appctl dpctl/dump-conntrack], [0], [])
+
+# vswitchd should not complaing about recirculation depth being exceeded,
+# which would indicate that packet is not properly dropped, but instead
+# loops between vswitchd flow tables until its ttl is down to 0.
+AT_CHECK([grep -qE 'Packet dropped. Max recirculation depth exceeded.' ovs-vswitchd.log], [1])
+
+OVS_APP_EXIT_AND_WAIT([ovn-controller])
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/.*error receiving.*/d
+/.*terminating with signal 15.*/d"])
+AT_CLEANUP
+])
+
 OVN_FOR_EACH_NORTHD([
 AT_SETUP([ovn -- ARP resolution for SNAT IP])
 ovn_start
