diff --git a/NEWS b/NEWS
index 5f448e67d..56261d9a5 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,7 @@ Post v21.09.0
     if not desired.
   - Added Load_Balancer_Group support, which simplifies large scale
     configurations of load balancers.
+  - Added Logical_Router_Port "gateway_mtu_bypass" option.
 
 OVN v21.09.0 - 01 Oct 2021
 --------------------------
diff --git a/northd/northd.c b/northd/northd.c
index 59286034d..7cb20fc93 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -10201,8 +10201,47 @@ build_adm_ctrl_flows_for_lrouter(
     }
 }
 
-static int
-build_check_pkt_len_action_string(struct ovn_port *op, struct ds *actions);
+/* All 'gateway_mtu' and 'gateway_mtu_bypass' flows should be built with this
+ * function.
+ */
+static void OVS_PRINTF_FORMAT(9, 10)
+build_gateway_mtu_flow(struct hmap *lflows, struct ovn_port *op,
+                       enum ovn_stage stage, uint16_t prio_low,
+                       uint16_t prio_high, struct ds *match,
+                       struct ds *actions, const struct ovsdb_idl_row *hint,
+                       const char *extra_actions_fmt, ...)
+{
+    int gw_mtu = smap_get_int(&op->nbrp->options, "gateway_mtu", 0);
+
+    va_list extra_actions_args;
+    va_start(extra_actions_args, extra_actions_fmt);
+
+    ds_clear(actions);
+    if (gw_mtu > 0) {
+        ds_put_format(actions, REGBIT_PKT_LARGER" = check_pkt_larger(%d); ",
+                      gw_mtu + VLAN_ETH_HEADER_LEN);
+    }
+
+    ds_put_format_valist(actions, extra_actions_fmt, extra_actions_args);
+    ovn_lflow_add_with_hint(lflows, op->od, stage, prio_low,
+                            ds_cstr(match), ds_cstr(actions),
+                            hint);
+
+    if (gw_mtu > 0) {
+        const char *gw_mtu_bypass = smap_get(&op->nbrp->options,
+                                             "gateway_mtu_bypass");
+        if (gw_mtu_bypass) {
+            ds_clear(actions);
+            ds_put_format_valist(actions, extra_actions_fmt,
+                                 extra_actions_args);
+            ds_put_format(match, " && (%s)", gw_mtu_bypass);
+            ovn_lflow_add_with_hint(lflows, op->od, stage, prio_high,
+                                    ds_cstr(match), ds_cstr(actions),
+                                    hint);
+        }
+    }
+    va_end(extra_actions_args);
+}
 
 /* Logical router ingress Table 0: L2 Admission Control
  * This table drops packets that the router shouldn’t see at all based
@@ -10230,17 +10269,12 @@ build_adm_ctrl_flows_for_lrouter_port(
          * This will save us from having to match on inport further down in
          * the pipeline.
          */
-        ds_clear(actions);
-
-        build_check_pkt_len_action_string(op, actions);
-        ds_put_format(actions, REG_INPORT_ETH_ADDR " = %s; next;",
-                      op->lrp_networks.ea_s);
-
         ds_clear(match);
         ds_put_format(match, "eth.mcast && inport == %s", op->json_key);
-        ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
-                                ds_cstr(match), ds_cstr(actions),
-                                &op->nbrp->header_);
+        build_gateway_mtu_flow(lflows, op, S_ROUTER_IN_ADMISSION, 50, 55,
+                               match, actions, &op->nbrp->header_,
+                               REG_INPORT_ETH_ADDR " = %s; next;",
+                               op->lrp_networks.ea_s);
 
         ds_clear(match);
         ds_put_format(match, "eth.dst == %s && inport == %s",
@@ -10251,9 +10285,10 @@ build_adm_ctrl_flows_for_lrouter_port(
             ds_put_format(match, " && is_chassis_resident(%s)",
                           op->cr_port->json_key);
         }
-        ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
-                                ds_cstr(match),  ds_cstr(actions),
-                                &op->nbrp->header_);
+        build_gateway_mtu_flow(lflows, op, S_ROUTER_IN_ADMISSION, 50, 55,
+                               match, actions, &op->nbrp->header_,
+                               REG_INPORT_ETH_ADDR " = %s; next;",
+                               op->lrp_networks.ea_s);
     }
 }
 
@@ -11248,40 +11283,21 @@ build_icmperr_pkt_big_flows(struct ovn_port *op, int mtu, struct hmap *lflows,
     free(outport_match);
 }
 
-static int
-build_check_pkt_len_action_string(struct ovn_port *op, struct ds *actions)
-{
-    int gw_mtu = smap_get_int(&op->nbrp->options, "gateway_mtu", 0);
-
-    if (gw_mtu > 0) {
-        /* Add the flows only if gateway_mtu is configured. */
-        ds_put_format(actions,
-                      REGBIT_PKT_LARGER" = check_pkt_larger(%d); ",
-                      gw_mtu + VLAN_ETH_HEADER_LEN);
-    }
-    return gw_mtu;
-}
-
 static void
 build_check_pkt_len_flows_for_lrp(struct ovn_port *op,
                                   struct hmap *lflows, struct hmap *ports,
                                   struct shash *meter_groups, struct ds *match,
                                   struct ds *actions)
 {
-    ds_clear(actions);
-    int gw_mtu = build_check_pkt_len_action_string(op, actions);
+    int gw_mtu = smap_get_int(&op->nbrp->options, "gateway_mtu", 0);
     if (gw_mtu <= 0) {
         return;
     }
 
-    ds_put_format(actions, "next;");
-
     ds_clear(match);
     ds_put_format(match, "outport == %s", op->json_key);
-
-    ovn_lflow_add_with_hint(lflows, op->od, S_ROUTER_IN_CHK_PKT_LEN, 50,
-                            ds_cstr(match), ds_cstr(actions),
-                            &op->nbrp->header_);
+    build_gateway_mtu_flow(lflows, op, S_ROUTER_IN_CHK_PKT_LEN, 50, 55,
+                           match, actions, &op->nbrp->header_, "next;");
 
     /* ingress traffic */
     build_icmperr_pkt_big_flows(op, gw_mtu, lflows, meter_groups,
@@ -12397,12 +12413,6 @@ build_lrouter_ingress_flow(struct hmap *lflows, struct ovn_datapath *od,
         * This will save us from having to match on inport further
         * down in the pipeline.
         */
-        ds_clear(actions);
-
-        build_check_pkt_len_action_string(od->l3dgw_ports[0], actions);
-        ds_put_format(actions, REG_INPORT_ETH_ADDR " = %s; next;",
-                      od->l3dgw_ports[0]->lrp_networks.ea_s);
-
         ds_clear(match);
         ds_put_format(match,
                       "eth.dst == "ETH_ADDR_FMT" && inport == %s"
@@ -12410,9 +12420,11 @@ build_lrouter_ingress_flow(struct hmap *lflows, struct ovn_datapath *od,
                       ETH_ADDR_ARGS(mac),
                       od->l3dgw_ports[0]->json_key,
                       nat->logical_port);
-        ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_ADMISSION, 50,
-                                ds_cstr(match), ds_cstr(actions),
-                                &nat->header_);
+        build_gateway_mtu_flow(lflows, od->l3dgw_ports[0],
+                               S_ROUTER_IN_ADMISSION, 50, 55,
+                               match, actions, &nat->header_,
+                               REG_INPORT_ETH_ADDR " = %s; next;",
+                               od->l3dgw_ports[0]->lrp_networks.ea_s);
     }
 }
 
diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
index b6a5f7767..129abeb9e 100644
--- a/northd/ovn-northd.8.xml
+++ b/northd/ovn-northd.8.xml
@@ -1984,6 +1984,13 @@ output;
           <code>check_pkt_larger</code> in order to mark the packet
           setting <code>REGBIT_PKT_LARGER</code> if the size is greater
           than the MTU.
+
+          If the port is also configured with
+          <code>options:gateway_mtu_bypass</code> then another flow is
+          added, with priority-55, to bypass the <code>check_pkt_larger</code>
+          flow.  This is useful for traffic that normally doesn't need to be
+          fragmented and for which check_pkt_larger, which might not be
+          offloadable, is not really needed.  One such example is TCP traffic.
         </p>
       </li>
 
@@ -3784,6 +3791,13 @@ REGBIT_PKT_LARGER = check_pkt_larger(<var>L</var>); next;
       <ref table="Logical_Router_Port" db="OVN_Northbound"/> row.
     </p>
 
+    <p>
+      If the port is also configured with
+      <code>options:gateway_mtu_bypass</code> then another flow is
+      added, with priority-55, to bypass the <code>check_pkt_larger</code>
+      flow.
+    </p>
+
     <p>
       This table adds one priority-0 fallback flow that matches all packets
       and advances to the next table.
diff --git a/ovn-nb.xml b/ovn-nb.xml
index e31578fb6..2296e9291 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -2799,6 +2799,29 @@
           prefix according to RFC3663
         </p>
       </column>
+
+      <column name="options" key="gateway_mtu">
+        <p>
+          Configures the maximum length, excluding VLAN and Ethernet header,
+          of packets that should be allowed to be received by distributed
+          and gateway router logical ports.  For all packets with a larger
+          size than this value ICMP (v4/v6) error packets are generated
+          indicating that fragmentation should be performed before sending
+          packets to the OVN port.
+        </p>
+      </column>
+
+      <column name="options" key="gateway_mtu_bypass">
+        <p>
+          When configured, represents a match expression, in the same
+          expression language used for the <ref column="match"
+          table="Logical_Flow" db="OVN_Southbound"/> column in the OVN
+          Southbound database's <ref table="Logical_Flow" db="OVN_Southbound"/>
+          table.  Packets matching this expression will bypass the length
+          check configured through the
+          <ref column="options" key="gateway_mtu"/> option.
+        </p>
+      </column>
     </group>
 
     <group title="Attachment">
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 193f465db..b929014b7 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -5075,7 +5075,7 @@ check ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public
 check ovn-nbctl --wait=sb lrp-set-gateway-chassis lr0-public ch1
 
 ovn-sbctl dump-flows lr0 > lr0flows
-AT_CAPTURE_FILE([sw0flows])
+AT_CAPTURE_FILE([lr0flows])
 
 AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
   table=??(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
@@ -5085,7 +5085,7 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=.
 check ovn-nbctl --wait=sb set logical_router_port lr0-public options:gateway_mtu=1500
 
 ovn-sbctl dump-flows lr0 > lr0flows
-AT_CAPTURE_FILE([sw0flows])
+AT_CAPTURE_FILE([lr0flows])
 
 AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
   table=??(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
@@ -5116,7 +5116,7 @@ check ovn-nbctl --wait=sb clear logical_router_port lr0-public gateway_chassis
 check ovn-nbctl --wait=sb set logical_router lr0 options:chassis=ch1
 
 ovn-sbctl dump-flows lr0 > lr0flows
-AT_CAPTURE_FILE([sw0flows])
+AT_CAPTURE_FILE([lr0flows])
 
 AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
   table=??(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
@@ -5140,16 +5140,41 @@ AT_CHECK([grep -E "lr_in_ip_input.*icmp6_error" lr0flows | sort], [0], [dnl
   table=3 (lr_in_ip_input     ), priority=150  , match=(inport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
 ])
 
+# Set gateway_mtu_bypass to avoid check_pkt_larger() for tcp on lr0-public.
+check ovn-nbctl --wait=sb set logical_router_port lr0-public options:gateway_mtu_bypass=tcp
+
+ovn-sbctl dump-flows lr0 > lr0flows
+AT_CAPTURE_FILE([lr0flows])
+
+AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
+  table=??(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
+  table=??(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); next;)
+  table=??(lr_in_chk_pkt_len  ), priority=55   , match=(outport == "lr0-public" && (tcp)), action=(next;)
+  table=??(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+])
+
+AT_CHECK([grep "lr_in_admission" lr0flows | grep -e "check_pkt_larger" -e "tcp" | sort], [0], [dnl
+  table=0 (lr_in_admission    ), priority=50   , match=(eth.dst == 00:00:20:20:12:13 && inport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); xreg0[[0..47]] = 00:00:20:20:12:13; next;)
+  table=0 (lr_in_admission    ), priority=50   , match=(eth.mcast && inport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); xreg0[[0..47]] = 00:00:20:20:12:13; next;)
+  table=0 (lr_in_admission    ), priority=55   , match=(eth.dst == 00:00:20:20:12:13 && inport == "lr0-public" && (tcp)), action=(xreg0[[0..47]] = 00:00:20:20:12:13; next;)
+  table=0 (lr_in_admission    ), priority=55   , match=(eth.mcast && inport == "lr0-public" && (tcp)), action=(xreg0[[0..47]] = 00:00:20:20:12:13; next;)
+])
+
 # Set gateway_mtu option on lr0-sw0
 check ovn-nbctl --wait=sb set logical_router_port lr0-sw0 options:gateway_mtu=1400
 
 ovn-sbctl dump-flows lr0 > lr0flows
-AT_CAPTURE_FILE([sw0flows])
+AT_CAPTURE_FILE([lr0flows])
 
 AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
   table=??(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
   table=??(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); next;)
   table=??(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1418); next;)
+  table=??(lr_in_chk_pkt_len  ), priority=55   , match=(outport == "lr0-public" && (tcp)), action=(next;)
   table=??(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
@@ -5161,7 +5186,7 @@ AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=.
   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
 ])
 
-AT_CHECK([grep -E "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
+AT_CHECK([grep "lr_in_admission.*check_pkt_larger" lr0flows | sort], [0], [dnl
   table=0 (lr_in_admission    ), priority=50   , match=(eth.dst == 00:00:00:00:ff:01 && inport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1418); xreg0[[0..47]] = 00:00:00:00:ff:01; next;)
   table=0 (lr_in_admission    ), priority=50   , match=(eth.dst == 00:00:20:20:12:13 && inport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); xreg0[[0..47]] = 00:00:20:20:12:13; next;)
   table=0 (lr_in_admission    ), priority=50   , match=(eth.mcast && inport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); xreg0[[0..47]] = 00:00:20:20:12:13; next;)
@@ -5177,14 +5202,49 @@ AT_CHECK([grep -E "lr_in_ip_input.*icmp6_error" lr0flows | sort], [0], [dnl
   table=3 (lr_in_ip_input     ), priority=150  , match=(inport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
 ])
 
+# Set gateway_mtu_bypass to avoid check_pkt_larger() for tcp on lr0-sw0.
+check ovn-nbctl --wait=sb set logical_router_port lr0-sw0 options:gateway_mtu_bypass=tcp
+
+ovn-sbctl dump-flows lr0 > lr0flows
+AT_CAPTURE_FILE([lr0flows])
+
+AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
+  table=??(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
+  table=??(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); next;)
+  table=??(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1418); next;)
+  table=??(lr_in_chk_pkt_len  ), priority=55   , match=(outport == "lr0-public" && (tcp)), action=(next;)
+  table=??(lr_in_chk_pkt_len  ), priority=55   , match=(outport == "lr0-sw0" && (tcp)), action=(next;)
+  table=??(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip4.dst = ip4.src; ip4.src = 10.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw0" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:01; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff01; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-public" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1500; next(pipeline=ingress, table=0); };)
+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip4.dst = ip4.src; ip4.src = 20.0.0.1; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
+  table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-sw1" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:00:00:ff:02; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe00:ff02; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
+])
+
+AT_CHECK([grep "lr_in_admission" lr0flows | grep -e "check_pkt_larger" -e "tcp" | sort], [0], [dnl
+  table=0 (lr_in_admission    ), priority=50   , match=(eth.dst == 00:00:00:00:ff:01 && inport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1418); xreg0[[0..47]] = 00:00:00:00:ff:01; next;)
+  table=0 (lr_in_admission    ), priority=50   , match=(eth.dst == 00:00:20:20:12:13 && inport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); xreg0[[0..47]] = 00:00:20:20:12:13; next;)
+  table=0 (lr_in_admission    ), priority=50   , match=(eth.mcast && inport == "lr0-public"), action=(reg9[[1]] = check_pkt_larger(1518); xreg0[[0..47]] = 00:00:20:20:12:13; next;)
+  table=0 (lr_in_admission    ), priority=50   , match=(eth.mcast && inport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1418); xreg0[[0..47]] = 00:00:00:00:ff:01; next;)
+  table=0 (lr_in_admission    ), priority=55   , match=(eth.dst == 00:00:00:00:ff:01 && inport == "lr0-sw0" && (tcp)), action=(xreg0[[0..47]] = 00:00:00:00:ff:01; next;)
+  table=0 (lr_in_admission    ), priority=55   , match=(eth.dst == 00:00:20:20:12:13 && inport == "lr0-public" && (tcp)), action=(xreg0[[0..47]] = 00:00:20:20:12:13; next;)
+  table=0 (lr_in_admission    ), priority=55   , match=(eth.mcast && inport == "lr0-public" && (tcp)), action=(xreg0[[0..47]] = 00:00:20:20:12:13; next;)
+  table=0 (lr_in_admission    ), priority=55   , match=(eth.mcast && inport == "lr0-sw0" && (tcp)), action=(xreg0[[0..47]] = 00:00:00:00:ff:01; next;)
+])
+
 # Clear gateway_mtu option on lr0-public
 check ovn-nbctl --wait=sb clear logical_router_port lr0-public options
 ovn-sbctl dump-flows lr0 > lr0flows
-AT_CAPTURE_FILE([sw0flows])
+AT_CAPTURE_FILE([lr0flows])
 
 AT_CHECK([grep -e "chk_pkt_len" -e "lr_in_larger_pkts" lr0flows | sed 's/table=../table=??/' | sort], [0], [dnl
   table=??(lr_in_chk_pkt_len  ), priority=0    , match=(1), action=(next;)
   table=??(lr_in_chk_pkt_len  ), priority=50   , match=(outport == "lr0-sw0"), action=(reg9[[1]] = check_pkt_larger(1418); next;)
+  table=??(lr_in_chk_pkt_len  ), priority=55   , match=(outport == "lr0-sw0" && (tcp)), action=(next;)
   table=??(lr_in_larger_pkts  ), priority=0    , match=(1), action=(next;)
   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip4 && reg9[[1]] && reg9[[0]] == 0), action=(icmp4_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip4.dst = ip4.src; ip4.src = 172.168.0.100; ip.ttl = 255; icmp4.type = 3; /* Destination Unreachable. */ icmp4.code = 4; /* Frag Needed and DF was Set. */ icmp4.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
   table=??(lr_in_larger_pkts  ), priority=150  , match=(inport == "lr0-public" && outport == "lr0-sw0" && ip6 && reg9[[1]] && reg9[[0]] == 0), action=(icmp6_error {reg9[[0]] = 1; reg9[[1]] = 0; eth.dst = 00:00:20:20:12:13; ip6.dst = ip6.src; ip6.src = fe80::200:20ff:fe20:1213; ip.ttl = 255; icmp6.type = 2; /* Packet Too Big. */ icmp6.code = 0; icmp6.frag_mtu = 1400; next(pipeline=ingress, table=0); };)
