Currently, there is one ct.est flow per LB VIP,
that was required to keep track if we need to
pass the "skip_snat" or "force_snat" flags.
However since c1d6b8ac ("northd: Store skip_snat and force_snat in 
ct_label/mark")
the flags are carried in the ct entry and
we can use match on them the same way we do
for related traffic.

Simplify the logic for established
traffic through load balancers, by removing
the requirement for one ct.est flow per VIP
and replacing them with three generic ct.est flows:
match=(ct.est && !ct.rel && !ct.new && ct_mark.natted), action=(next;)
match=(ct.est && !ct.rel && !ct.new && ct_mark.natted && ct_mark.force_snat == 
1), action=(flags.force_snat_for_lb = 1; next;)
match=(ct.est && !ct.rel && !ct.new && ct_mark.natted && ct_mark.skip_snat == 
1), action=(flags.skip_snat_for_lb = 1; next;)

This allows us avoiding of matching on L4
in defrag stage by not storing the L3 and L4
destination in registers. Match directly on
L3 and L4 destination for ct.new in DNAT stage.

Populate the registers in LB affinity check stage
as they are needed for LB affinity learn.

Reported-at: https://bugzilla.redhat.com/2172048
Reported-at: https://bugzilla.redhat.com/2170885
Signed-off-by: Ales Musil <[email protected]>
---
 northd/northd.c          | 157 ++++++++++-----------
 northd/ovn-northd.8.xml  | 136 +++++-------------
 tests/ovn-northd.at      | 295 ++++++++++++++++++++-------------------
 tests/ovn.at             |  10 +-
 tests/system-ovn-kmod.at | 136 ++++++++++++++++++
 5 files changed, 399 insertions(+), 335 deletions(-)

diff --git a/northd/northd.c b/northd/northd.c
index ab10fea6a..b80a22916 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -7093,7 +7093,9 @@ build_lb_rules_pre_stateful(struct hmap *lflows, struct 
ovn_northd_lb *lb,
  * - load balancing affinity check:
  *   table=lr_in_lb_aff_check, priority=100
  *      match=(new_lb_match)
- *      action=(REGBIT_KNOWN_LB_SESSION = chk_lb_aff(); next;)
+ *      action=(REG_NEXT_HOP_IPV4 = ip4.dst;
+ *              REG_ORIG_TP_DPORT_ROUTER = tcp.dst;
+ *              REGBIT_KNOWN_LB_SESSION = chk_lb_aff(); next;)
  *
  * - load balancing:
  *   table=lr_in_dnat, priority=150
@@ -7134,16 +7136,11 @@ build_lb_affinity_lr_flows(struct hmap *lflows, struct 
ovn_northd_lb *lb,
         return;
     }
 
-    static char *aff_check = REGBIT_KNOWN_LB_SESSION" = chk_lb_aff(); next;";
-
-    ovn_lflow_add_with_dp_group(
-        lflows, dp_bitmap, S_ROUTER_IN_LB_AFF_CHECK, 100,
-        new_lb_match, aff_check, &lb->nlb->header_);
-
     struct ds aff_action = DS_EMPTY_INITIALIZER;
     struct ds aff_action_learn = DS_EMPTY_INITIALIZER;
     struct ds aff_match = DS_EMPTY_INITIALIZER;
     struct ds aff_match_learn = DS_EMPTY_INITIALIZER;
+    struct ds aff_check_action = DS_EMPTY_INITIALIZER;
 
     bool ipv6 = !IN6_IS_ADDR_V4MAPPED(&lb_vip->vip);
     const char *ip_match = ipv6 ? "ip6" : "ip4";
@@ -7159,6 +7156,20 @@ build_lb_affinity_lr_flows(struct hmap *lflows, struct 
ovn_northd_lb *lb,
         ct_flag = "; force_snat";
     }
 
+    /* Create affinity check flow. */
+    ds_put_format(&aff_check_action, "%s = %s.dst; ", reg_vip, ip_match);
+
+    if (lb_vip->port_str) {
+        ds_put_format(&aff_check_action, REG_ORIG_TP_DPORT_ROUTER" = %s.dst; ",
+                      lb->proto);
+    }
+    ds_put_cstr(&aff_check_action, REGBIT_KNOWN_LB_SESSION
+                " = chk_lb_aff(); next;");
+
+    ovn_lflow_add_with_dp_group(
+        lflows, dp_bitmap, S_ROUTER_IN_LB_AFF_CHECK, 100,
+        new_lb_match, ds_cstr(&aff_check_action), &lb->nlb->header_);
+
     /* Prepare common part of affinity LB and affinity learn action. */
     ds_put_format(&aff_action, "%s = %s; ", reg_vip, lb_vip->vip_str);
     ds_put_cstr(&aff_action_learn, "commit_lb_aff(vip = \"");
@@ -7256,6 +7267,7 @@ build_lb_affinity_lr_flows(struct hmap *lflows, struct 
ovn_northd_lb *lb,
     ds_destroy(&aff_action_learn);
     ds_destroy(&aff_match);
     ds_destroy(&aff_match_learn);
+    ds_destroy(&aff_check_action);
 }
 
 /* Builds the logical switch flows related to load balancer affinity.
@@ -10454,10 +10466,8 @@ enum lrouter_nat_lb_flow_type {
 
 struct lrouter_nat_lb_flows_ctx {
     const char *new_action[LROUTER_NAT_LB_FLOW_MAX];
-    const char *est_action[LROUTER_NAT_LB_FLOW_MAX];
 
     struct ds *new_match;
-    struct ds *est_match;
     struct ds *undnat_match;
 
     struct ovn_lb_vip *lb_vip;
@@ -10475,10 +10485,21 @@ build_distr_lrouter_nat_flows_for_lb(struct 
lrouter_nat_lb_flows_ctx *ctx,
                                      enum lrouter_nat_lb_flow_type type,
                                      struct ovn_datapath *od)
 {
-    char *gw_action = od->is_gw_router ? "ct_dnat;" : "ct_dnat_in_czone;";
+    char *undnat_action;
+    switch (type) {
+    case LROUTER_NAT_LB_FLOW_FORCE_SNAT:
+        undnat_action = "flags.force_snat_for_lb = 1; next;";
+        break;
+    case LROUTER_NAT_LB_FLOW_SKIP_SNAT:
+        undnat_action = "flags.skip_snat_for_lb = 1; next;";
+        break;
+    case LROUTER_NAT_LB_FLOW_NORMAL:
+    case LROUTER_NAT_LB_FLOW_MAX:
+        undnat_action = od->is_gw_router ? "ct_dnat;" : "ct_dnat_in_czone;";
+        break;
+    }
     /* Store the match lengths, so we can reuse the ds buffer. */
     size_t new_match_len = ctx->new_match->length;
-    size_t est_match_len = ctx->est_match->length;
     size_t undnat_match_len = ctx->undnat_match->length;
 
 
@@ -10491,33 +10512,24 @@ build_distr_lrouter_nat_flows_for_lb(struct 
lrouter_nat_lb_flows_ctx *ctx,
     if (ctx->lb_vip->n_backends || !ctx->lb_vip->empty_backend_rej) {
         ds_put_format(ctx->new_match, " && is_chassis_resident(%s)",
                       od->l3dgw_ports[0]->cr_port->json_key);
-        ds_put_format(ctx->est_match, " && is_chassis_resident(%s)",
-                      od->l3dgw_ports[0]->cr_port->json_key);
     }
 
     ovn_lflow_add_with_hint__(ctx->lflows, od, S_ROUTER_IN_DNAT, ctx->prio,
                               ds_cstr(ctx->new_match), ctx->new_action[type],
                               NULL, meter, &ctx->lb->nlb->header_);
-    ovn_lflow_add_with_hint(ctx->lflows, od, S_ROUTER_IN_DNAT, ctx->prio,
-                            ds_cstr(ctx->est_match), ctx->est_action[type],
-                            &ctx->lb->nlb->header_);
 
     ds_truncate(ctx->new_match, new_match_len);
-    ds_truncate(ctx->est_match, est_match_len);
 
     if (!ctx->lb_vip->n_backends) {
         return;
     }
 
-    const char *action = (type == LROUTER_NAT_LB_FLOW_NORMAL)
-                         ? gw_action : ctx->est_action[type];
-
     ds_put_format(ctx->undnat_match,
                   ") && outport == %s && is_chassis_resident(%s)",
                   od->l3dgw_ports[0]->json_key,
                   od->l3dgw_ports[0]->cr_port->json_key);
     ovn_lflow_add_with_hint(ctx->lflows, od, S_ROUTER_OUT_UNDNAT, 120,
-                            ds_cstr(ctx->undnat_match), action,
+                            ds_cstr(ctx->undnat_match), undnat_action,
                             &ctx->lb->nlb->header_);
     ds_truncate(ctx->undnat_match, undnat_match_len);
 }
@@ -10560,11 +10572,6 @@ build_gw_lrouter_nat_flows_for_lb(struct 
lrouter_nat_lb_flows_ctx *ctx,
             ctx->new_action[type], &ctx->lb->nlb->header_);
     }
     bitmap_free(dp_non_meter);
-
-    ovn_lflow_add_with_dp_group(
-        ctx->lflows, dp_bitmap, S_ROUTER_IN_DNAT, ctx->prio,
-        ds_cstr(ctx->est_match), ctx->est_action[type],
-        &ctx->lb->nlb->header_);
 }
 
 static void
@@ -10576,19 +10583,13 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip 
*lb_vip,
                                const struct shash *meter_groups,
                                const struct chassis_features *features)
 {
-    const char *ct_natted = features->ct_no_masked_label
-                            ? "ct_mark.natted"
-                            : "ct_label.natted";
-
     bool ipv4 = lb_vip->address_family == AF_INET;
     const char *ip_match = ipv4 ? "ip4" : "ip6";
-    const char *ip_reg = ipv4 ? REG_NEXT_HOP_IPV4 : REG_NEXT_HOP_IPV6;
 
     int prio = 110;
 
     struct ds skip_snat_act = DS_EMPTY_INITIALIZER;
     struct ds force_snat_act = DS_EMPTY_INITIALIZER;
-    struct ds est_match = DS_EMPTY_INITIALIZER;
     struct ds undnat_match = DS_EMPTY_INITIALIZER;
     struct ds unsnat_match = DS_EMPTY_INITIALIZER;
 
@@ -10605,19 +10606,14 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip 
*lb_vip,
      * of "ct_lb_mark($targets);". The other flow is for ct.est with
      * an action of "next;".
      */
-    ds_put_format(match, "ct.new && !ct.rel && %s && %s == %s",
-                  ip_match, ip_reg, lb_vip->vip_str);
+    ds_put_format(match, "ct.new && !ct.rel && %s && %s.dst == %s",
+                  ip_match, ip_match, lb_vip->vip_str);
     if (lb_vip->port_str) {
         prio = 120;
-        ds_put_format(match, " && %s && "REG_ORIG_TP_DPORT_ROUTER" == %s",
-                      lb->proto, lb_vip->port_str);
+        ds_put_format(match, " && %s && %s.dst == %s",
+                      lb->proto, lb->proto, lb_vip->port_str);
     }
 
-    ds_put_cstr(&est_match, "ct.est");
-    /* Clone the match after initial "ct.new" (6 bytes). */
-    ds_put_cstr(&est_match, ds_cstr(match) + 6);
-    ds_put_format(&est_match, " && %s == 1", ct_natted);
-
     /* Add logical flows to UNDNAT the load balanced reverse traffic in
      * the router egress pipleine stage - S_ROUTER_OUT_UNDNAT if the logical
      * router has a gateway router port associated.
@@ -10654,20 +10650,12 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip 
*lb_vip,
         .lflows = lflows,
         .meter_groups = meter_groups,
         .new_match = match,
-        .est_match = &est_match,
         .undnat_match = &undnat_match
     };
 
     ctx.new_action[LROUTER_NAT_LB_FLOW_NORMAL] = ds_cstr(action);
-    ctx.est_action[LROUTER_NAT_LB_FLOW_NORMAL] = "next;";
-
     ctx.new_action[LROUTER_NAT_LB_FLOW_SKIP_SNAT] = ds_cstr(&skip_snat_act);
-    ctx.est_action[LROUTER_NAT_LB_FLOW_SKIP_SNAT] =
-                                        "flags.skip_snat_for_lb = 1; next;";
-
     ctx.new_action[LROUTER_NAT_LB_FLOW_FORCE_SNAT] = ds_cstr(&force_snat_act);
-    ctx.est_action[LROUTER_NAT_LB_FLOW_FORCE_SNAT] =
-                                        "flags.force_snat_for_lb = 1; next;";
 
     enum {
         LROUTER_NAT_LB_AFF            = LROUTER_NAT_LB_FLOW_MAX,
@@ -10750,7 +10738,6 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip 
*lb_vip,
 
     ds_destroy(&unsnat_match);
     ds_destroy(&undnat_match);
-    ds_destroy(&est_match);
     ds_destroy(&skip_snat_act);
     ds_destroy(&force_snat_act);
 
@@ -10824,39 +10811,19 @@ build_lrouter_defrag_flows_for_lb(struct 
ovn_northd_lb *lb,
         return;
     }
 
-    struct ds defrag_actions = DS_EMPTY_INITIALIZER;
     for (size_t i = 0; i < lb->n_vips; i++) {
         struct ovn_lb_vip *lb_vip = &lb->vips[i];
+        bool ipv6 = lb_vip->address_family == AF_INET6;
         int prio = 100;
 
-        ds_clear(&defrag_actions);
         ds_clear(match);
-
-        if (lb_vip->address_family == AF_INET) {
-            ds_put_format(match, "ip && ip4.dst == %s", lb_vip->vip_str);
-            ds_put_format(&defrag_actions, REG_NEXT_HOP_IPV4" = %s; ",
-                          lb_vip->vip_str);
-        } else {
-            ds_put_format(match, "ip && ip6.dst == %s", lb_vip->vip_str);
-            ds_put_format(&defrag_actions, REG_NEXT_HOP_IPV6" = %s; ",
-                          lb_vip->vip_str);
-        }
-
-        if (lb_vip->port_str) {
-            ds_put_format(match, " && %s", lb->proto);
-            prio = 110;
-
-            ds_put_format(&defrag_actions, REG_ORIG_TP_DPORT_ROUTER
-                          " = %s.dst; ", lb->proto);
-        }
-
-        ds_put_format(&defrag_actions, "ct_dnat;");
+        ds_put_format(match, "ip && ip%c.dst == %s", ipv6 ? '6' : '4',
+                      lb_vip->vip_str);
 
         ovn_lflow_add_with_dp_group(
             lflows, lb->nb_lr_map, S_ROUTER_IN_DEFRAG, prio,
-            ds_cstr(match), ds_cstr(&defrag_actions), &lb->nlb->header_);
+            ds_cstr(match), "ct_dnat;", &lb->nlb->header_);
     }
-    ds_destroy(&defrag_actions);
 }
 
 static void
@@ -14118,10 +14085,10 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath 
*od, struct hmap *lflows,
     ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 0, "1", "next;");
     ovn_lflow_add(lflows, od, S_ROUTER_IN_ECMP_STATEFUL, 0, "1", "next;");
 
-    /* Ingress DNAT and DEFRAG Table (Priority 50/70).
-     *
-     * The defrag stage needs to have flows for ICMP in order to get
-     * the correct ct_state that can be used by DNAT stage.
+    const char *ct_flag_reg = features->ct_no_masked_label
+                              ? "ct_mark"
+                              : "ct_label";
+    /* Ingress DNAT (Priority 50/70).
      *
      * Allow traffic that is related to an existing conntrack entry.
      * At the same time apply NAT for this traffic.
@@ -14132,16 +14099,10 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath 
*od, struct hmap *lflows,
      * that's generated from a non-listening UDP port.  */
     if (od->has_lb_vip && features->ct_lb_related) {
         ds_clear(match);
-        const char *ct_flag_reg = features->ct_no_masked_label
-                                  ? "ct_mark"
-                                  : "ct_label";
 
         ds_put_cstr(match, "ct.rel && !ct.est && !ct.new");
         size_t match_len = match->length;
 
-        ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 50, "icmp || icmp6",
-                      "ct_dnat;");
-
         ds_put_format(match, " && %s.skip_snat == 1", ct_flag_reg);
         ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 70, ds_cstr(match),
                       "flags.skip_snat_for_lb = 1; ct_commit_nat;");
@@ -14152,10 +14113,34 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath 
*od, struct hmap *lflows,
                       "flags.force_snat_for_lb = 1; ct_commit_nat;");
 
         ds_truncate(match, match_len);
-        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50,
-                      "ct.rel && !ct.est && !ct.new", "ct_commit_nat;");
+        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50, ds_cstr(match),
+                      "ct_commit_nat;");
+    }
 
+    /* Ingress DNAT (Priority 50/70).
+     *
+     * Pass the traffic that is already established to the next table with
+     * proper flags set.
+     */
+    if (od->has_lb_vip) {
         ds_clear(match);
+
+        ds_put_format(match, "ct.est && !ct.rel && !ct.new && %s.natted",
+                      ct_flag_reg);
+        size_t match_len = match->length;
+
+        ds_put_format(match, " && %s.skip_snat == 1", ct_flag_reg);
+        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 70, ds_cstr(match),
+                      "flags.skip_snat_for_lb = 1; next;");
+
+        ds_truncate(match, match_len);
+        ds_put_format(match, " && %s.force_snat == 1", ct_flag_reg);
+        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 70, ds_cstr(match),
+                      "flags.force_snat_for_lb = 1; next;");
+
+        ds_truncate(match, match_len);
+        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50, ds_cstr(match),
+                      "next;");
     }
 
     /* If the router has load balancer or DNAT rules, re-circulate every packet
diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
index 131345e85..61b19af5c 100644
--- a/northd/ovn-northd.8.xml
+++ b/northd/ovn-northd.8.xml
@@ -3292,35 +3292,16 @@ icmp6 {
     </p>
 
     <p>
-      If load balancing rules with only virtual IP addresses are configured in
+      For all load balancing rules that are configured in
       <code>OVN_Northbound</code> database for a Gateway router,
       a priority-100 flow is added for each configured virtual IP address
       <var>VIP</var>. For IPv4 <var>VIPs</var> the flow matches
       <code>ip &amp;&amp; ip4.dst == <var>VIP</var></code>.  For IPv6
       <var>VIPs</var>, the flow matches <code>ip &amp;&amp; ip6.dst ==
-      <var>VIP</var></code>. The flow applies the action <code>reg0 =
-      <var>VIP</var>; ct_dnat;</code>  (or <code>xxreg0</code> for IPv6) to
-      send IP packets to the connection tracker for packet de-fragmentation and
-      to dnat the destination IP for the committed connection before sending it
-      to the next table.
-    </p>
-
-    <p>
-      If load balancing rules with virtual IP addresses and ports are
-      configured in <code>OVN_Northbound</code> database for a Gateway router,
-      a priority-110 flow is added for each configured virtual IP address
-      <var>VIP</var>, protocol <var>PROTO</var> and port <var>PORT</var>.
-      For IPv4 <var>VIPs</var> the flow matches
-      <code>ip &amp;&amp; ip4.dst == <var>VIP</var> &amp;&amp;
-      <var>PROTO</var> &amp;&amp; <var>PROTO</var>.dst ==
-      <var>PORT</var></code>. For IPv6 <var>VIPs</var>, the flow matches
-      <code>ip &amp;&amp; ip6.dst == <var>VIP</var> &amp;&amp;
-      <var>PROTO</var> &amp;&amp; <var>PROTO</var>.dst ==
-      <var>PORT</var></code>. The flow applies the action <code>reg0 =
-      <var>VIP</var>; reg9[16..31] = <var>PROTO</var>.dst; ct_dnat;</code>
-      (or <code>xxreg0</code> for IPv6) to send IP packets to the connection
-      tracker for packet de-fragmentation and to dnat the destination IP for
-      the committed connection before sending it to the next table.
+      <var>VIP</var></code>. The flow applies the action <code> ct_dnat;</code>
+      to send IP packets to the connection tracker for packet de-fragmentation
+      and to dnat the destination IP for the committed connection before
+      sending it to the next table.
     </p>
 
     <p>
@@ -3359,10 +3340,11 @@ icmp6 {
         column, that includes a L4 port <var>PORT</var> of protocol
         <var>P</var> and IPv4 or IPv6 address <var>VIP</var>, a priority-100
         flow that matches on <code>ct.new &amp;&amp; ip &amp;&amp;
-        reg0 == <var>VIP</var> &amp;&amp; <var>P</var> &amp;&amp; reg9[16..31]
+        ip.dst == <var>VIP</var> &amp;&amp; <var>P</var> &amp;&amp; P.dst
         == </code> <code><var>PORT</var></code> (<code>xxreg0 == <var>VIP
-        </var></code> in the IPv6 case) with an action of <code>reg9[6] =
-        chk_lb_aff(); next;</code>
+        </var></code> in the IPv6 case) with an action of <code>reg0 = ip.dst;
+        reg9[16..31] = P.dst; reg9[6] = chk_lb_aff(); next;</code>
+        (<code>xxreg0 == <var>ip6.dst</var> </code> in the IPv6 case)
       </li>
 
       <li>
@@ -3395,9 +3377,8 @@ icmp6 {
         column, that includes a L4 port <var>PORT</var> of protocol
         <var>P</var> and IPv4 or IPv6 address <var>VIP</var>, a priority-150
         flow that matches on <code>reg9[6] == 1 &amp;&amp; ct.new &amp;&amp;
-        ip &amp;&amp; reg0 == <var>VIP</var> &amp;&amp; <var>P</var> &amp;&amp;
-        reg9[16..31] == </code> <code><var>PORT</var></code> (<code>xxreg0
-        == <var>VIP</var></code> in the IPv6 case) with an action of
+        ip &amp;&amp; ip.dst == <var>VIP</var> &amp;&amp; <var>P</var> 
&amp;&amp;
+        P.dst == </code> <code><var>PORT</var></code> with an action of
         <code>ct_lb_mark(<var>args</var>) </code>, where <var>args</var>
         contains comma separated IP addresses (and optional port numbers)
         to load balance to.  The address family of the IP addresses of
@@ -3420,56 +3401,25 @@ icmp6 {
           Router with gateway port in <code>OVN_Northbound</code> database that
           includes a L4 port <var>PORT</var> of protocol <var>P</var> and IPv4
           or IPv6 address <var>VIP</var>, a priority-120 flow that matches on
-          <code>ct.new &amp;&amp; !ct.rel &amp;&amp; ip &amp;&amp; reg0 ==
-          <var>VIP</var> &amp;&amp; <var>P</var> &amp;&amp; reg9[16..31] ==
-          </code> <code><var>PORT</var></code> (<code>xxreg0 == <var>VIP</var>
-          </code> in the IPv6 case) with an action of
+          <code>ct.new &amp;&amp; !ct.rel &amp;&amp; ip &amp;&amp; ip.dst ==
+          <var>VIP</var> &amp;&amp; <var>P</var> &amp;&amp; P.dst ==
+          </code> <code><var>PORT</var></code> with an action of
           <code>ct_lb_mark(<var>args</var>)</code>, where <var>args</var> 
contains
           comma separated IPv4 or IPv6 addresses (and optional port numbers) to
           load balance to.  If the router is configured to force SNAT any
           load-balanced packets, the above action will be replaced by
-          <code>flags.force_snat_for_lb = 1; 
ct_lb_mark(<var>args</var>);</code>.
+          <code>flags.force_snat_for_lb = 1; ct_lb_mark(<var>args</var>;
+          force_snat);</code>.
           If the load balancing rule is configured with <code>skip_snat</code>
           set to true, the above action will be replaced by
-          <code>flags.skip_snat_for_lb = 1; 
ct_lb_mark(<var>args</var>);</code>.
+          <code>flags.skip_snat_for_lb = 1; ct_lb_mark(<var>args</var>;
+          skip_snat);</code>.
           If health check is enabled, then
           <var>args</var> will only contain those endpoints whose service
           monitor status entry in <code>OVN_Southbound</code> db is
           either <code>online</code> or empty.
         </p>
 
-        <p>
-          The previous table <code>lr_in_defrag</code> sets the register
-          <code>reg0</code> (or <code>xxreg0</code> for IPv6) and does
-          <code>ct_dnat</code>.  Hence for established traffic, this
-          table just advances the packet to the next stage.
-        </p>
-      </li>
-
-      <li>
-        <p>
-          For all the configured load balancing rules for a router in
-          <code>OVN_Northbound</code> database that includes a L4 port
-          <var>PORT</var> of protocol <var>P</var> and IPv4 or IPv6 address
-          <var>VIP</var>, a priority-120 flow that matches on
-          <code>ct.est &amp;&amp; !ct.rel &amp;&amp; ip4 &amp;&amp; reg0 ==
-          <var>VIP</var> &amp;&amp; <var>P</var> &amp;&amp; reg9[16..31] ==
-          </code> <code><var>PORT</var></code> (<code>ip6</code> and
-          <code>xxreg0 == <var>VIP</var></code> in the IPv6 case) with an
-          action of <code>next;</code>. If the router is configured to force
-          SNAT any load-balanced packets, the above action will be replaced by
-          <code>flags.force_snat_for_lb = 1; next;</code>. If the load
-          balancing rule is configured with <code>skip_snat</code> set to true,
-          the above action will be replaced by
-          <code>flags.skip_snat_for_lb = 1; next;</code>.
-        </p>
-
-        <p>
-          The previous table <code>lr_in_defrag</code> sets the register
-          <code>reg0</code> (or <code>xxreg0</code> for IPv6) and does
-          <code>ct_dnat</code>.  Hence for established traffic, this
-          table just advances the packet to the next stage.
-        </p>
       </li>
 
       <li>
@@ -3477,42 +3427,17 @@ icmp6 {
           For all the configured load balancing rules for a router in
           <code>OVN_Northbound</code> database that includes just an IP address
           <var>VIP</var> to match on, a priority-110 flow that matches on
-          <code>ct.new &amp;&amp; !ct.rel &amp;&amp; ip4 &amp;&amp; reg0 ==
-          <var>VIP</var></code> (<code>ip6</code> and <code>xxreg0 ==
-          <var>VIP</var></code> in the IPv6 case) with an action of
+          <code>ct.new &amp;&amp; !ct.rel &amp;&amp; ip4 &amp;&amp; ip.dst ==
+          <var>VIP</var></code> with an action of
           <code>ct_lb_mark(<var>args</var>)</code>, where <var>args</var> 
contains
           comma separated IPv4 or IPv6 addresses.  If the router is configured
           to force SNAT any load-balanced packets, the above action will be
           replaced by <code>flags.force_snat_for_lb = 1;
-          ct_lb_mark(<var>args</var>);</code>.
+          ct_lb_mark(<var>args</var>; force_snat);</code>.
           If the load balancing rule is configured with <code>skip_snat</code>
           set to true, the above action will be replaced by
-          <code>flags.skip_snat_for_lb = 1; 
ct_lb_mark(<var>args</var>);</code>.
-        </p>
-
-        <p>
-          The previous table <code>lr_in_defrag</code> sets the register
-          <code>reg0</code> (or <code>xxreg0</code> for IPv6) and does
-          <code>ct_dnat</code>.  Hence for established traffic, this
-          table just advances the packet to the next stage.
-        </p>
-      </li>
-
-
-      <li>
-        <p>
-          For all the configured load balancing rules for a router in
-          <code>OVN_Northbound</code> database that includes just an IP address
-          <var>VIP</var> to match on, a priority-110 flow that matches on
-          <code>ct.est &amp;&amp; !ct.rel &amp;&amp; ip4 &amp;&amp; reg0 ==
-          <var>VIP</var></code> (or <code>ip6</code> and
-          <code>xxreg0 == <var>VIP</var></code>) with an action of
-          <code>next;</code>. If the router is configured to force SNAT any
-          load-balanced packets, the above action will be replaced by
-          <code>flags.force_snat_for_lb = 1; next;</code>.
-          If the load balancing rule is configured with <code>skip_snat</code>
-          set to true, the above action will be replaced by
-          <code>flags.skip_snat_for_lb = 1; next;</code>.
+          <code>flags.skip_snat_for_lb = 1; ct_lb_mark(<var>args</var>;
+          skip_snat);</code>.
         </p>
 
         <p>
@@ -3539,7 +3464,20 @@ icmp6 {
             with an action of <code>ct_commit_nat;</code>, if the router
             has load balancer assigned to it. Along with two priority 70 flows
             that match <code>skip_snat</code> and <code>force_snat</code>
-            flags.
+            flags, setting the <code>flags.force_snat_for_lb = 1</code> or
+            <code>flags.skip_snat_for_lb = 1</code> accordingly.
+        </p>
+      </li>
+      <li>
+        <p>
+          For the established traffic, a priority 50 flow that matches
+          <code>ct.est &amp;&amp; !ct.rel &amp;&amp; !ct.new &amp;&amp;
+          ct_mark.natted</code> with an action of <code>next;</code>,
+          if the router has load balancer assigned to it. Along with two
+          priority 70 flows that match <code>skip_snat</code> and
+          <code>force_snat</code> flags, setting the
+          <code>flags.force_snat_for_lb = 1</code> or
+          <code>flags.skip_snat_for_lb = 1</code> accordingly.
         </p>
       </li>
     </ul>
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index d0f6893e9..99e20d6b2 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -3757,18 +3757,18 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows | sort], [0], 
[dnl
 
 AT_CHECK([grep "lr_in_defrag" lr0flows | sort], [0], [dnl
   table=5 (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.10 && tcp), action=(reg0 = 10.0.0.10; reg9[[16..31]] = tcp.dst; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.100 && tcp), action=(reg0 = 10.0.0.100; reg9[[16..31]] = tcp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=50   , match=(icmp || icmp6), 
action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.10), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.100), action=(ct_dnat;)
 ])
 
 AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.100 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80), 
action=(ct_lb_mark(backends=10.0.0.4:8080);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.100 && tcp && reg9[[16..31]] == 80), 
action=(ct_lb_mark(backends=10.0.0.40:8080);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80), 
action=(ct_lb_mark(backends=10.0.0.4:8080);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80), 
action=(ct_lb_mark(backends=10.0.0.40:8080);)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -3788,18 +3788,18 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows | sort], [0], 
[dnl
 
 AT_CHECK([grep "lr_in_defrag" lr0flows | sort], [0], [dnl
   table=5 (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.10 && tcp), action=(reg0 = 10.0.0.10; reg9[[16..31]] = tcp.dst; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.100 && tcp), action=(reg0 = 10.0.0.100; reg9[[16..31]] = tcp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=50   , match=(icmp || icmp6), 
action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.10), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.100), action=(ct_dnat;)
 ])
 
 AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.100 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080; 
force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.100 && tcp && reg9[[16..31]] == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080; 
force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080; 
force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080; 
force_snat);)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -3838,18 +3838,18 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows | sort], [0], 
[dnl
 
 AT_CHECK([grep "lr_in_defrag" lr0flows | sort], [0], [dnl
   table=5 (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.10 && tcp), action=(reg0 = 10.0.0.10; reg9[[16..31]] = tcp.dst; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.100 && tcp), action=(reg0 = 10.0.0.100; reg9[[16..31]] = tcp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=50   , match=(icmp || icmp6), 
action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.10), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.100), action=(ct_dnat;)
 ])
 
 AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.100 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080; 
force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.100 && tcp && reg9[[16..31]] == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080; 
force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080; 
force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080; 
force_snat);)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -3902,18 +3902,18 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows | sort], [0], 
[dnl
 
 AT_CHECK([grep "lr_in_defrag" lr0flows | sort], [0], [dnl
   table=5 (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.10 && tcp), action=(reg0 = 10.0.0.10; reg9[[16..31]] = tcp.dst; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.100 && tcp), action=(reg0 = 10.0.0.100; reg9[[16..31]] = tcp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=50   , match=(icmp || icmp6), 
action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.10), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.100), action=(ct_dnat;)
 ])
 
 AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.100 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080; 
force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.100 && tcp && reg9[[16..31]] == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080; 
force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080; 
force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080; 
force_snat);)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -3953,14 +3953,13 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows | sort], [0], 
[dnl
 
 AT_CHECK([grep "lr_in_defrag" lr0flows | sort], [0], [dnl
   table=5 (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.100 && tcp), action=(reg0 = 10.0.0.100; reg9[[16..31]] = tcp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.20 && tcp), action=(reg0 = 10.0.0.20; reg9[[16..31]] = tcp.dst; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=50   , match=(icmp || icmp6), 
action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.100), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.20), action=(ct_dnat;)
 ])
 
 AT_CHECK([grep "lr_in_dnat" lr0flows | grep skip_snat_for_lb | sort], [0], [dnl
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.20 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(flags.skip_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.20 && tcp && reg9[[16..31]] == 80), 
action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080; 
skip_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.20 && tcp && tcp.dst == 80), 
action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080; 
skip_snat);)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
 
@@ -5212,25 +5211,23 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows | sort], [0], 
[dnl
 
 AT_CHECK([grep "lr_in_defrag" lr0flows | sort], [0], [dnl
   table=5 (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.200), action=(reg0 = 172.168.0.200; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.10 && tcp), action=(reg0 = 10.0.0.10; reg9[[16..31]] = tcp.dst; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.100 && tcp), action=(reg0 = 172.168.0.100; reg9[[16..31]] = tcp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.210 && udp), action=(reg0 = 172.168.0.210; reg9[[16..31]] = udp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=50   , match=(icmp || icmp6), 
action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.10), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.100), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.200), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.210), action=(ct_dnat;)
 ])
 
 AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
   table=7 (lr_in_dnat         ), priority=100  , match=(ip && ip4.dst == 
172.168.0.20 && inport == "lr0-public" && 
is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.200 && ct_mark.natted == 1 && 
is_chassis_resident("cr-lr0-public")), action=(next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.200 && is_chassis_resident("cr-lr0-public")), 
action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 1 
&& is_chassis_resident("cr-lr0-public")), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.100 && tcp && reg9[[16..31]] == 8082 && ct_mark.natted 
== 1 && is_chassis_resident("cr-lr0-public")), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && udp && reg9[[16..31]] == 60 && ct_mark.natted 
== 1 && is_chassis_resident("cr-lr0-public")), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80 && 
is_chassis_resident("cr-lr0-public")), 
action=(ct_lb_mark(backends=10.0.0.4:8080);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.100 && tcp && reg9[[16..31]] == 8082 && 
is_chassis_resident("cr-lr0-public")), 
action=(ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && udp && reg9[[16..31]] == 60 && 
is_chassis_resident("cr-lr0-public")), 
action=(ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062);)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.200 && is_chassis_resident("cr-lr0-public")), 
action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && 
is_chassis_resident("cr-lr0-public")), 
action=(ct_lb_mark(backends=10.0.0.4:8080);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8082 && 
is_chassis_resident("cr-lr0-public")), 
action=(ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.210 && udp && udp.dst == 60 && 
is_chassis_resident("cr-lr0-public")), 
action=(ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062);)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -5285,25 +5282,23 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows | sort], [0], 
[dnl
 
 AT_CHECK([grep "lr_in_defrag" lr0flows | sort], [0], [dnl
   table=5 (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.200), action=(reg0 = 172.168.0.200; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.10 && tcp), action=(reg0 = 10.0.0.10; reg9[[16..31]] = tcp.dst; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.100 && tcp), action=(reg0 = 172.168.0.100; reg9[[16..31]] = tcp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.210 && udp), action=(reg0 = 172.168.0.210; reg9[[16..31]] = udp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=50   , match=(icmp || icmp6), 
action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.10), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.100), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.200), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.210), action=(ct_dnat;)
 ])
 
 AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
   table=7 (lr_in_dnat         ), priority=100  , match=(ip && ip4.dst == 
172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.200 && ct_mark.natted == 1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.200), 
action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.100 && tcp && reg9[[16..31]] == 8082 && ct_mark.natted 
== 1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && udp && reg9[[16..31]] == 60 && ct_mark.natted 
== 1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80), 
action=(ct_lb_mark(backends=10.0.0.4:8080);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.100 && tcp && reg9[[16..31]] == 8082), 
action=(ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && udp && reg9[[16..31]] == 60), 
action=(ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062);)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.200), 
action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80), 
action=(ct_lb_mark(backends=10.0.0.4:8080);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8082), 
action=(ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.210 && udp && udp.dst == 60), 
action=(ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062);)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -5350,25 +5345,23 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows | sort], [0], 
[dnl
 
 AT_CHECK([grep "lr_in_defrag" lr0flows | sort], [0], [dnl
   table=5 (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.200), action=(reg0 = 172.168.0.200; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.10 && tcp), action=(reg0 = 10.0.0.10; reg9[[16..31]] = tcp.dst; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.100 && tcp), action=(reg0 = 172.168.0.100; reg9[[16..31]] = tcp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.210 && udp), action=(reg0 = 172.168.0.210; reg9[[16..31]] = udp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=50   , match=(icmp || icmp6), 
action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.10), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.100), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.200), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.210), action=(ct_dnat;)
 ])
 
 AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
   table=7 (lr_in_dnat         ), priority=100  , match=(ip && ip4.dst == 
172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.200 && ct_mark.natted == 1), 
action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.200), action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.100 && tcp && reg9[[16..31]] == 8082 && ct_mark.natted 
== 1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && udp && reg9[[16..31]] == 60 && ct_mark.natted 
== 1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080; 
force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.100 && tcp && reg9[[16..31]] == 8082), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82; force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && udp && reg9[[16..31]] == 60), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062; force_snat);)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.200), action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080; 
force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8082), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82; force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.210 && udp && udp.dst == 60), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062; force_snat);)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -5417,28 +5410,25 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows | sort], [0], 
[dnl
 
 AT_CHECK([grep "lr_in_defrag" lr0flows | sort], [0], [dnl
   table=5 (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.200), action=(reg0 = 172.168.0.200; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.10 && tcp), action=(reg0 = 10.0.0.10; reg9[[16..31]] = tcp.dst; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.10 && tcp), action=(reg0 = 172.168.0.10; reg9[[16..31]] = tcp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.100 && tcp), action=(reg0 = 172.168.0.100; reg9[[16..31]] = tcp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.210 && udp), action=(reg0 = 172.168.0.210; reg9[[16..31]] = udp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=50   , match=(icmp || icmp6), 
action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.10), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.10), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.100), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.200), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.210), action=(ct_dnat;)
 ])
 
 AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
   table=7 (lr_in_dnat         ), priority=100  , match=(ip && ip4.dst == 
172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.200 && ct_mark.natted == 1), 
action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.200), action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.10 && tcp && reg9[[16..31]] == 9082 && ct_mark.natted 
== 1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.100 && tcp && reg9[[16..31]] == 8082 && ct_mark.natted 
== 1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && udp && reg9[[16..31]] == 60 && ct_mark.natted 
== 1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080; 
force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.10 && tcp && reg9[[16..31]] == 9082), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82; force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.100 && tcp && reg9[[16..31]] == 8082), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82; force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && udp && reg9[[16..31]] == 60), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062; force_snat);)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.200), action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080; 
force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.10 && tcp && tcp.dst == 9082), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82; force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8082), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82; force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.210 && udp && udp.dst == 60), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062; force_snat);)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -5497,31 +5487,27 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows | sort], [0], 
[dnl
 
 AT_CHECK([grep "lr_in_defrag" lr0flows | sort], [0], [dnl
   table=5 (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.200), action=(reg0 = 172.168.0.200; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
10.0.0.10 && tcp), action=(reg0 = 10.0.0.10; reg9[[16..31]] = tcp.dst; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.10 && tcp), action=(reg0 = 172.168.0.10; reg9[[16..31]] = tcp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.100 && tcp), action=(reg0 = 172.168.0.100; reg9[[16..31]] = tcp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.210 && udp), action=(reg0 = 172.168.0.210; reg9[[16..31]] = udp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip6.dst == 
def0::2 && tcp), action=(xxreg0 = def0::2; reg9[[16..31]] = tcp.dst; ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=50   , match=(icmp || icmp6), 
action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
10.0.0.10), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.10), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.100), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.200), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.210), action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip6.dst == 
def0::2), action=(ct_dnat;)
 ])
 
 AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
   table=7 (lr_in_dnat         ), priority=100  , match=(ip && ip4.dst == 
172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.200 && ct_mark.natted == 1), 
action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.200), action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.10 && tcp && reg9[[16..31]] == 9082 && ct_mark.natted 
== 1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.100 && tcp && reg9[[16..31]] == 8082 && ct_mark.natted 
== 1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && udp && reg9[[16..31]] == 60 && ct_mark.natted 
== 1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip6 && xxreg0 == def0::2 && tcp && reg9[[16..31]] == 8000 && ct_mark.natted == 
1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080; 
force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.10 && tcp && reg9[[16..31]] == 9082), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82; force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.100 && tcp && reg9[[16..31]] == 8082), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82; force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && udp && reg9[[16..31]] == 60), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062; force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip6 && xxreg0 == def0::2 && tcp && reg9[[16..31]] == 8000), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=[[aef0::2]]:80,[[aef0::3]]:80; force_snat);)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.200), action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80), 
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080; 
force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.10 && tcp && tcp.dst == 9082), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82; force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8082), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82; force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.210 && udp && udp.dst == 60), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062; force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip6 && ip6.dst == def0::2 && tcp && tcp.dst == 8000), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=[[aef0::2]]:80,[[aef0::3]]:80; force_snat);)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -5573,18 +5559,17 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows | sort], [0], 
[dnl
 
 AT_CHECK([grep "lr_in_defrag" lr0flows | sort], [0], [dnl
   table=5 (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.210 && tcp), action=(reg0 = 172.168.0.210; reg9[[16..31]] = tcp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=110  , match=(ip && ip4.dst == 
172.168.0.210 && udp), action=(reg0 = 172.168.0.210; reg9[[16..31]] = udp.dst; 
ct_dnat;)
-  table=5 (lr_in_defrag       ), priority=50   , match=(icmp || icmp6), 
action=(ct_dnat;)
+  table=5 (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
172.168.0.210), action=(ct_dnat;)
 ])
 
 AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && tcp && reg9[[16..31]] == 60 && ct_mark.natted 
== 1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && udp && reg9[[16..31]] == 60 && ct_mark.natted 
== 1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && tcp && reg9[[16..31]] == 60), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062; force_snat);)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.0.210 && udp && reg9[[16..31]] == 60), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062; force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.210 && tcp && tcp.dst == 60), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062; force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.0.210 && udp && udp.dst == 60), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.50:6062,10.0.0.60:6062; force_snat);)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -5635,9 +5620,11 @@ ovn-sbctl set service_monitor $sm_vip2 status=offline
 
 AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.10.10 && ct_mark.natted == 1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.10.10), action=(reg0 = 0; reject { outport <-> inport; 
next(pipeline=egress,table=3);};)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.10.10), action=(reg0 = 0; reject { outport <-> 
inport; next(pipeline=egress,table=3);};)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -5647,9 +5634,11 @@ check ovn-nbctl --wait=sb set load_balancer lb5 
options:skip_snat=true
 
 AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.10.10 && ct_mark.natted == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.10.10), action=(flags.skip_snat_for_lb = 1; reg0 = 0; 
reject { outport <-> inport; next(pipeline=egress,table=3);};)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.10.10), action=(flags.skip_snat_for_lb = 1; reg0 = 0; 
reject { outport <-> inport; next(pipeline=egress,table=3);};)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -5661,9 +5650,11 @@ check ovn-nbctl --wait=sb set logical_router lr0 
options:lb_force_snat_ip="route
 
 AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.168.10.10 && ct_mark.natted == 1), 
action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.168.10.10), action=(flags.force_snat_for_lb = 1; reg0 = 0; 
reject { outport <-> inport; next(pipeline=egress,table=3);};)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.168.10.10), action=(flags.force_snat_for_lb = 1; reg0 = 
0; reject { outport <-> inport; next(pipeline=egress,table=3);};)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -7887,8 +7878,10 @@ check ovn-nbctl                                          
     \
 AS_BOX([No chassis registered - use ct_lb_mark and ct_mark.natted])
 check ovn-nbctl --wait=sb sync
 AT_CHECK([ovn-sbctl lflow-list | grep -e natted -e ct_lb], [0], [dnl
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 66.66.66.66 && ct_mark.natted == 1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 66.66.66.66), action=(ct_lb_mark(backends=42.42.42.2);)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 66.66.66.66), action=(ct_lb_mark(backends=42.42.42.2);)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=6 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && 
ip4.dst == 66.66.66.66), action=(reg1 = 66.66.66.66; ct_lb_mark;)
   table=6 (ls_in_pre_stateful ), priority=110  , match=(reg0[[2]] == 1), 
action=(ct_lb_mark;)
   table=12(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
66.66.66.66), action=(reg0[[1]] = 0; ct_lb_mark(backends=42.42.42.2);)
@@ -7899,8 +7892,10 @@ AS_BOX([Chassis registered that doesn't support 
ct_lb_mark - use ct_lb and ct_la
 check ovn-sbctl chassis-add hv geneve 127.0.0.1
 check ovn-nbctl --wait=sb sync
 AT_CHECK([ovn-sbctl lflow-list | grep -e natted -e ct_lb], [0], [dnl
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 66.66.66.66 && ct_label.natted == 1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 66.66.66.66), action=(ct_lb(backends=42.42.42.2);)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 66.66.66.66), action=(ct_lb(backends=42.42.42.2);)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_label.natted && ct_label.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_label.natted && ct_label.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_label.natted), action=(next;)
   table=6 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && 
ip4.dst == 66.66.66.66), action=(reg1 = 66.66.66.66; ct_lb;)
   table=6 (ls_in_pre_stateful ), priority=110  , match=(reg0[[2]] == 1), 
action=(ct_lb;)
   table=12(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
66.66.66.66), action=(reg0[[1]] = 0; ct_lb(backends=42.42.42.2);)
@@ -7911,8 +7906,10 @@ AS_BOX([Chassis upgrades and supports ct_lb_mark - use 
ct_lb_mark and ct_mark.na
 check ovn-sbctl set chassis hv other_config:ct-no-masked-label=true
 check ovn-nbctl --wait=sb sync
 AT_CHECK([ovn-sbctl lflow-list | grep -e natted -e ct_lb], [0], [dnl
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 66.66.66.66 && ct_mark.natted == 1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 66.66.66.66), action=(ct_lb_mark(backends=42.42.42.2);)
+  table=7 (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 66.66.66.66), action=(ct_lb_mark(backends=42.42.42.2);)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=6 (ls_in_pre_stateful ), priority=120  , match=(reg0[[2]] == 1 && 
ip4.dst == 66.66.66.66), action=(reg1 = 66.66.66.66; ct_lb_mark;)
   table=6 (ls_in_pre_stateful ), priority=110  , match=(reg0[[2]] == 1), 
action=(ct_lb_mark;)
   table=12(ls_in_lb           ), priority=110  , match=(ct.new && ip4.dst == 
66.66.66.66), action=(reg0[[1]] = 0; ct_lb_mark(backends=42.42.42.2);)
@@ -8245,15 +8242,17 @@ AT_CAPTURE_FILE([R1flows])
 
 AT_CHECK([grep "lr_in_lb_aff_check" R1flows | sort], [0], [dnl
   table=6 (lr_in_lb_aff_check ), priority=0    , match=(1), action=(next;)
-  table=6 (lr_in_lb_aff_check ), priority=100  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.16.0.10 && tcp && reg9[[16..31]] == 80), action=(reg9[[6]] = 
chk_lb_aff(); next;)
+  table=6 (lr_in_lb_aff_check ), priority=100  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80), action=(reg0 = ip4.dst; 
reg9[[16..31]] = tcp.dst; reg9[[6]] = chk_lb_aff(); next;)
 ])
 AT_CHECK([grep "lr_in_dnat " R1flows | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.16.0.10 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.16.0.10 && tcp && reg9[[16..31]] == 80), 
action=(ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80), 
action=(ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);)
   table=7 (lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && 
ct.new && ip4 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 
172.16.0.10; ct_lb_mark(backends=10.0.0.2:80);)
   table=7 (lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && 
ct.new && ip4 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 
172.16.0.10; ct_lb_mark(backends=20.0.0.2:80);)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -8271,11 +8270,13 @@ AT_CAPTURE_FILE([R1flows_skip_snat])
 
 AT_CHECK([grep "lr_in_dnat " R1flows_skip_snat | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.16.0.10 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(flags.skip_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.16.0.10 && tcp && reg9[[16..31]] == 80), 
action=(flags.skip_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80; skip_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80), 
action=(flags.skip_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80; skip_snat);)
   table=7 (lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && 
ct.new && ip4 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 
172.16.0.10; flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80; 
skip_snat);)
   table=7 (lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && 
ct.new && ip4 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 
172.16.0.10; flags.skip_snat_for_lb = 1; ct_lb_mark(backends=20.0.0.2:80; 
skip_snat);)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -8290,11 +8291,13 @@ AT_CAPTURE_FILE([R1flows_force_snat])
 
 AT_CHECK([grep "lr_in_dnat " R1flows_force_snat | sort], [0], [dnl
   table=7 (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 172.16.0.10 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 
1), action=(flags.force_snat_for_lb = 1; next;)
-  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 172.16.0.10 && tcp && reg9[[16..31]] == 80), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80; force_snat);)
+  table=7 (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80), 
action=(flags.force_snat_for_lb = 1; 
ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80; force_snat);)
   table=7 (lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && 
ct.new && ip4 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 
172.16.0.10; flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80; 
force_snat);)
   table=7 (lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 && 
ct.new && ip4 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), action=(reg0 = 
172.16.0.10; flags.force_snat_for_lb = 1; ct_lb_mark(backends=20.0.0.2:80; 
force_snat);)
+  table=7 (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=7 (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=7 (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=7 (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -8570,12 +8573,13 @@ ovn-sbctl dump-flows | DUMP_FLOWS_SORTED > lflows0
 
 AT_CHECK([grep -e "lr_in_defrag" -e "lr_in_dnat" lflows0], [0], [dnl
   table=? (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=? (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
192.168.0.1), action=(reg0 = 192.168.0.1; ct_dnat;)
-  table=? (lr_in_defrag       ), priority=50   , match=(icmp || icmp6), 
action=(ct_dnat;)
+  table=? (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
192.168.0.1), action=(ct_dnat;)
   table=? (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=? (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 192.168.0.1 && ct_mark.natted == 1), action=(next;)
-  table=? (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 192.168.0.1), action=(ct_lb_mark(backends=192.168.1.10);)
+  table=? (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 192.168.0.1), action=(ct_lb_mark(backends=192.168.1.10);)
+  table=? (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=? (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=? (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=? (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=? (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=? (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
@@ -8600,10 +8604,12 @@ ovn-sbctl dump-flows | DUMP_FLOWS_SORTED > lflows1
 
 AT_CHECK([grep -e "lr_in_defrag" -e "lr_in_dnat" lflows1], [0], [dnl
   table=? (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=? (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
192.168.0.1), action=(reg0 = 192.168.0.1; ct_dnat;)
+  table=? (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
192.168.0.1), action=(ct_dnat;)
   table=? (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=? (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 192.168.0.1 && ct_label.natted == 1), action=(next;)
-  table=? (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 192.168.0.1), action=(ct_lb(backends=192.168.1.10);)
+  table=? (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 192.168.0.1), action=(ct_lb(backends=192.168.1.10);)
+  table=? (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_label.natted), action=(next;)
+  table=? (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_label.natted && ct_label.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=? (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_label.natted && ct_label.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
 ])
 
 AT_CHECK([grep -e "ls_in_acl" -e "ls_out_acl" lflows1 | grep 
"priority=65532"], [0], [dnl
@@ -8626,12 +8632,13 @@ ovn-sbctl dump-flows | DUMP_FLOWS_SORTED > lflows2
 
 AT_CHECK([grep -e "lr_in_defrag" -e "lr_in_dnat" lflows2], [0], [dnl
   table=? (lr_in_defrag       ), priority=0    , match=(1), action=(next;)
-  table=? (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
192.168.0.1), action=(reg0 = 192.168.0.1; ct_dnat;)
-  table=? (lr_in_defrag       ), priority=50   , match=(icmp || icmp6), 
action=(ct_dnat;)
+  table=? (lr_in_defrag       ), priority=100  , match=(ip && ip4.dst == 
192.168.0.1), action=(ct_dnat;)
   table=? (lr_in_dnat         ), priority=0    , match=(1), action=(next;)
-  table=? (lr_in_dnat         ), priority=110  , match=(ct.est && !ct.rel && 
ip4 && reg0 == 192.168.0.1 && ct_mark.natted == 1), action=(next;)
-  table=? (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && reg0 == 192.168.0.1), action=(ct_lb_mark(backends=192.168.1.10);)
+  table=? (lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel && 
ip4 && ip4.dst == 192.168.0.1), action=(ct_lb_mark(backends=192.168.1.10);)
+  table=? (lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted), action=(next;)
   table=? (lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  table=? (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.force_snat == 1), 
action=(flags.force_snat_for_lb = 1; next;)
+  table=? (lr_in_dnat         ), priority=70   , match=(ct.est && !ct.rel && 
!ct.new && ct_mark.natted && ct_mark.skip_snat == 1), 
action=(flags.skip_snat_for_lb = 1; next;)
   table=? (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.force_snat == 1), action=(flags.force_snat_for_lb = 1; 
ct_commit_nat;)
   table=? (lr_in_dnat         ), priority=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
diff --git a/tests/ovn.at b/tests/ovn.at
index 40d701bb2..204b8e668 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -24329,7 +24329,7 @@ AT_CAPTURE_FILE([sbflows2])
 OVS_WAIT_FOR_OUTPUT(
   [ovn-sbctl dump-flows > sbflows2
    ovn-sbctl dump-flows lr0 | grep ct_lb_mark | grep priority=120 | sed 
's/table=..//'], 0,
-  [  (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip4 && 
reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80 && 
is_chassis_resident("cr-lr0-public")), 
action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80; 
hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
+  [  (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip4 && 
ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && 
is_chassis_resident("cr-lr0-public")), 
action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80; 
hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
 ])
 
 # get the svc monitor mac.
@@ -24371,8 +24371,7 @@ AT_CHECK(
 AT_CAPTURE_FILE([sbflows4])
 ovn-sbctl dump-flows lr0 > sbflows4
 AT_CHECK([grep lr_in_dnat sbflows4 | grep priority=120 | sed 's/table=..//' | 
sort], [0], [dnl
-  (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && ip4 && 
reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 1 && 
is_chassis_resident("cr-lr0-public")), action=(next;)
-  (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip4 && 
reg0 == 10.0.0.10 && tcp && reg9[[16..31]] == 80 && 
is_chassis_resident("cr-lr0-public")), action=(drop;)
+  (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip4 && 
ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && 
is_chassis_resident("cr-lr0-public")), action=(drop;)
 ])
 
 # Delete sw0-p1
@@ -24528,7 +24527,7 @@ AT_CAPTURE_FILE([sbflows2])
 OVS_WAIT_FOR_OUTPUT(
   [ovn-sbctl dump-flows > sbflows2
    ovn-sbctl dump-flows lr0 | grep ct_lb_mark | grep priority=120 | sed 
's/table=..//'], 0,
-  [  (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip6 && 
xxreg0 == 2001::a && tcp && reg9[[16..31]] == 80 && 
is_chassis_resident("cr-lr0-public")), 
action=(ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; 
hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
+  [  (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip6 && 
ip6.dst == 2001::a && tcp && tcp.dst == 80 && 
is_chassis_resident("cr-lr0-public")), 
action=(ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; 
hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
 ])
 
 # get the svc monitor mac.
@@ -24570,8 +24569,7 @@ AT_CHECK(
 AT_CAPTURE_FILE([sbflows4])
 ovn-sbctl dump-flows lr0 > sbflows4
 AT_CHECK([grep lr_in_dnat sbflows4 | grep priority=120 | sed 's/table=..//' | 
sort], [0], [dnl
-  (lr_in_dnat         ), priority=120  , match=(ct.est && !ct.rel && ip6 && 
xxreg0 == 2001::a && tcp && reg9[[16..31]] == 80 && ct_mark.natted == 1 && 
is_chassis_resident("cr-lr0-public")), action=(next;)
-  (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip6 && 
xxreg0 == 2001::a && tcp && reg9[[16..31]] == 80 && 
is_chassis_resident("cr-lr0-public")), action=(drop;)
+  (lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel && ip6 && 
ip6.dst == 2001::a && tcp && tcp.dst == 80 && 
is_chassis_resident("cr-lr0-public")), action=(drop;)
 ])
 
 # Delete sw0-p1
diff --git a/tests/system-ovn-kmod.at b/tests/system-ovn-kmod.at
index dd4996041..3c3e5bc61 100644
--- a/tests/system-ovn-kmod.at
+++ b/tests/system-ovn-kmod.at
@@ -215,3 +215,139 @@ as
 OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
 /connection dropped.*/d"])
 AT_CLEANUP
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([LB correctly de-fragments traffic])
+AT_KEYWORDS([ovnlb])
+
+CHECK_CONNTRACK()
+CHECK_CONNTRACK_NAT()
+AT_SKIP_IF([test $HAVE_SCAPY = no])
+
+ovn_start
+OVS_TRAFFIC_VSWITCHD_START()
+ADD_BR([br-int])
+ADD_BR([br-ext])
+
+# Logical network:
+# 2 logical switches "public" (192.168.1.0/24) and "internal" (172.16.1.0/24)
+# connected to a router lr.
+# internal has a server.
+# client is connected through localnet.
+#
+# Load balancer for udp 192.168.1.20:4242 172.16.1.2 4242.
+
+check ovs-ofctl add-flow br-ext action=normal
+# Set external-ids in br-int needed for ovn-controller
+check 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 \
+        -- set Open_vSwitch . external-ids:ovn-bridge-mappings=phynet:br-ext
+
+
+# Start ovn-controller
+start_daemon ovn-controller
+
+check ovn-nbctl lr-add lr
+check ovn-nbctl ls-add internal
+check ovn-nbctl ls-add public
+
+check ovn-nbctl lrp-add lr lr-pub 00:00:01:01:02:03 192.168.1.1/24
+check ovn-nbctl lsp-add  public pub-lr -- set Logical_Switch_Port pub-lr \
+    type=router options:router-port=lr-pub addresses=\"00:00:01:01:02:03\"
+
+check ovn-nbctl lrp-add lr lr-internal 00:00:01:01:02:04 172.16.1.1/24
+check ovn-nbctl lsp-add internal internal-lr -- set Logical_Switch_Port 
internal-lr \
+    type=router options:router-port=lr-internal addresses=\"00:00:01:01:02:04\"
+
+ovn-nbctl lsp-add public ln_port \
+                -- lsp-set-addresses ln_port unknown \
+                -- lsp-set-type ln_port localnet \
+                -- lsp-set-options ln_port network_name=phynet
+
+ADD_NAMESPACES(client)
+ADD_VETH(client, client, br-ext, "192.168.1.2/24", "f0:00:00:01:02:03", \
+         "192.168.1.1")
+
+ADD_NAMESPACES(server)
+ADD_VETH(server, server, br-int, "172.16.1.2/24", "f0:00:0f:01:02:03", \
+         "172.16.1.1")
+check ovn-nbctl lsp-add internal server \
+-- lsp-set-addresses server "f0:00:0f:01:02:03 172.16.1.2"
+
+# Config OVN load-balancer with a VIP.
+check ovn-nbctl lb-add lb1 192.168.1.20:4242 172.16.1.2:4242 udp
+check ovn-nbctl lr-lb-add lr lb1
+check ovn-nbctl set logical_router lr options:chassis=hv1
+check ovn-nbctl set logical_router_port lr-internal options:gateway_mtu=800
+
+ovn-nbctl --wait=hv sync
+
+NETNS_DAEMONIZE([server], [nc -l -u 172.16.1.2 4242 > /dev/null], [server.pid])
+
+# Collect ICMP packets on client side
+NETNS_DAEMONIZE([client], [tcpdump -l -U -i client -vnne \
+icmp > client.pcap 2>client_err], [tcpdump0.pid])
+OVS_WAIT_UNTIL([grep "listening" client_err])
+
+# Collect UDP packets on server side
+NETNS_DAEMONIZE([server], [tcpdump -l -U -i server -vnne \
+'udp and ip[[6:2]] > 0 and not ip[[6]] = 64' > server.pcap 2>server_err], 
[tcpdump1.pid])
+OVS_WAIT_UNTIL([grep "listening" server_err])
+
+check ip netns exec client python3 << EOF
+import os
+import socket
+import sys
+import time
+
+FILE="client.pcap"
+
+
+def contains_string(file, str):
+    file = open(file, "r")
+    for line in file.readlines():
+        if str in line:
+            return True
+    return False
+
+
+def need_frag_received():
+    for _ in range(20):
+        if os.path.getsize(FILE) and contains_string(FILE, "need to frag"):
+            return True
+        time.sleep(0.5)
+    return False
+
+
+sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+sock.sendto(b"x" * 1000, ("192.168.1.20", 4242))
+if need_frag_received():
+    sock.sendto(b"x" * 1000, ("192.168.1.20", 4242))
+else:
+    print("Missing need frag")
+    sys.exit(1)
+EOF
+
+OVS_WAIT_UNTIL([test "$(cat server.pcap | wc -l)" = "4"])
+
+
+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([NORTHD_TYPE])
+
+as
+OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+/connection dropped.*/d"])
+AT_CLEANUP
+])
-- 
2.39.2

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

Reply via email to