In order to have the SNAT action available for
related traffic store the flag in CT register.

Extend the ct_lb and ct_lb_mark action to
allow additional parameter that sets the
corresponding flag if needed in ct_label/ct_mark
register. This allows us to later on match on it
for related traffic and set the corresponding flags
fro additional flows.

Currently only two flags are supported "skip_snat"
and "force_snat" which are mutually exclusive.

Reported-at: https://bugzilla.redhat.com/2126083
Signed-off-by: Ales Musil <[email protected]>
---
 include/ovn/actions.h        |  7 +++
 include/ovn/logical-fields.h |  4 ++
 lib/actions.c                | 57 ++++++++++++++++++----
 lib/logical-fields.c         | 20 ++++++++
 northd/northd.c              | 55 ++++++++++++++++++---
 northd/ovn-northd.8.xml      |  4 +-
 ovn-sb.xml                   |  9 ++--
 tests/ovn-northd.at          | 87 +++++++++++++++++++++------------
 tests/ovn.at                 | 93 ++++++++++++++++++++++++++----------
 tests/system-ovn.at          | 51 +++++++++++++++-----
 10 files changed, 300 insertions(+), 87 deletions(-)

diff --git a/include/ovn/actions.h b/include/ovn/actions.h
index a56351081..7bbcabc30 100644
--- a/include/ovn/actions.h
+++ b/include/ovn/actions.h
@@ -284,6 +284,12 @@ struct ovnact_ct_nat {
     uint8_t ltable;             /* Logical table ID of next table. */
 };
 
+enum ovnact_ct_lb_flag {
+    OVNACT_CT_LB_FLAG_NONE,
+    OVNACT_CT_LB_FLAG_SKIP_SNAT,
+    OVNACT_CT_LB_FLAG_FORCE_SNAT,
+};
+
 struct ovnact_ct_lb_dst {
     int family;
     union {
@@ -300,6 +306,7 @@ struct ovnact_ct_lb {
     size_t n_dsts;
     uint8_t ltable;             /* Logical table ID of next table. */
     char *hash_fields;
+    enum ovnact_ct_lb_flag ct_flag;
 };
 
 struct ovnact_select_dst {
diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
index 8060488f9..a7b64ef67 100644
--- a/include/ovn/logical-fields.h
+++ b/include/ovn/logical-fields.h
@@ -182,9 +182,13 @@ const struct ovn_field *ovn_field_from_name(const char 
*name);
 
 #define OVN_CT_BLOCKED_BIT 0
 #define OVN_CT_NATTED_BIT  1
+#define OVN_CT_LB_SKIP_SNAT_BIT 2
+#define OVN_CT_LB_FORCE_SNAT_BIT 3
 
 #define OVN_CT_BLOCKED 1
 #define OVN_CT_NATTED  2
+#define OVN_CT_LB_SKIP_SNAT 4
+#define OVN_CT_LB_FORCE_SNAT 8
 
 #define OVN_CT_ECMP_ETH_1ST_BIT 32
 #define OVN_CT_ECMP_ETH_END_BIT 79
diff --git a/lib/actions.c b/lib/actions.c
index 47ec654e1..a5cba994c 100644
--- a/lib/actions.c
+++ b/lib/actions.c
@@ -1200,6 +1200,7 @@ parse_ct_lb_action(struct action_context *ctx, bool 
ct_lb_mark)
     size_t allocated_dsts = 0;
     size_t n_dsts = 0;
     char *hash_fields = NULL;
+    enum ovnact_ct_lb_flag ct_flag = OVNACT_CT_LB_FLAG_NONE;
 
     if (lexer_match(ctx->lexer, LEX_T_LPAREN) &&
         !lexer_match(ctx->lexer, LEX_T_RPAREN)) {
@@ -1280,8 +1281,7 @@ parse_ct_lb_action(struct action_context *ctx, bool 
ct_lb_mark)
 
         if (lexer_match_id(ctx->lexer, "hash_fields")) {
             if (!lexer_match(ctx->lexer, LEX_T_EQUALS) ||
-                ctx->lexer->token.type != LEX_T_STRING ||
-                lexer_lookahead(ctx->lexer) != LEX_T_RPAREN) {
+                ctx->lexer->token.type != LEX_T_STRING) {
                 lexer_syntax_error(ctx->lexer, "invalid hash_fields");
                 free(dsts);
                 return;
@@ -1289,6 +1289,16 @@ parse_ct_lb_action(struct action_context *ctx, bool 
ct_lb_mark)
 
             hash_fields = xstrdup(ctx->lexer->token.s);
             lexer_get(ctx->lexer);
+            if (!lexer_match(ctx->lexer, LEX_T_SEMICOLON)) {
+                lexer_get(ctx->lexer);
+            }
+        }
+
+        if (lexer_match_id(ctx->lexer, "skip_snat")) {
+            ct_flag = OVNACT_CT_LB_FLAG_SKIP_SNAT;
+            lexer_get(ctx->lexer);
+        } else if (lexer_match_id(ctx->lexer, "force_snat")) {
+            ct_flag = OVNACT_CT_LB_FLAG_FORCE_SNAT;
             lexer_get(ctx->lexer);
         }
     }
@@ -1299,6 +1309,7 @@ parse_ct_lb_action(struct action_context *ctx, bool 
ct_lb_mark)
     cl->dsts = dsts;
     cl->n_dsts = n_dsts;
     cl->hash_fields = hash_fields;
+    cl->ct_flag = ct_flag;
 }
 
 static void
@@ -1332,12 +1343,23 @@ format_ct_lb(const struct ovnact_ct_lb *cl, struct ds 
*s, bool ct_lb_mark)
                 }
             }
         }
-        ds_put_char(s, ')');
 
         if (cl->hash_fields) {
-            ds_chomp(s, ')');
-            ds_put_format(s, "; hash_fields=\"%s\")", cl->hash_fields);
+            ds_put_format(s, "; hash_fields=\"%s\"", cl->hash_fields);
+        }
+
+        switch (cl->ct_flag) {
+            case OVNACT_CT_LB_FLAG_SKIP_SNAT:
+                ds_put_cstr(s, "; skip_snat");
+                break;
+            case OVNACT_CT_LB_FLAG_FORCE_SNAT:
+                ds_put_cstr(s, "; force_snat");
+                break;
+            case OVNACT_CT_LB_FLAG_NONE:
+                /* None is the default value not shown in the output. */
+                break;
         }
+        ds_put_char(s, ')');
     }
 
     ds_put_char(s, ';');
@@ -1397,6 +1419,21 @@ encode_ct_lb(const struct ovnact_ct_lb *cl,
     struct ofpact_group *og;
     uint32_t zone_reg = ep->is_switch ? MFF_LOG_CT_ZONE - MFF_REG0
                             : MFF_LOG_DNAT_ZONE - MFF_REG0;
+    const char *flag_reg = ct_lb_mark ? "ct_mark" : "ct_label";
+
+    const char *ct_flag_value;
+    switch (cl->ct_flag) {
+        case OVNACT_CT_LB_FLAG_SKIP_SNAT:
+            ct_flag_value = OVN_CT_MASKED_STR(OVN_CT_LB_SKIP_SNAT);
+            break;
+        case OVNACT_CT_LB_FLAG_FORCE_SNAT:
+            ct_flag_value = OVN_CT_MASKED_STR(OVN_CT_LB_FORCE_SNAT);
+            break;
+        case OVNACT_CT_LB_FLAG_NONE:
+        default:
+            ct_flag_value = NULL;
+            break;
+    }
 
     struct ds ds = DS_EMPTY_INITIALIZER;
     ds_put_format(&ds, "type=select,selection_method=%s",
@@ -1428,9 +1465,13 @@ encode_ct_lb(const struct ovnact_ct_lb *cl,
         ds_put_format(&ds, "),commit,table=%d,zone=NXM_NX_REG%d[0..15],"
                       "exec(set_field:"
                         OVN_CT_MASKED_STR(OVN_CT_NATTED)
-                      "->%s))",
-                      recirc_table, zone_reg,
-                      ct_lb_mark ? "ct_mark" : "ct_label");
+                      "->%s",
+                      recirc_table, zone_reg, flag_reg);
+        if (ct_flag_value) {
+            ds_put_format(&ds, ",set_field:%s->%s", ct_flag_value, flag_reg);
+        }
+
+        ds_put_cstr(&ds, "))");
     }
 
     table_id = ovn_extend_table_assign_id(ep->group_table, ds_cstr(&ds),
diff --git a/lib/logical-fields.c b/lib/logical-fields.c
index fc131791e..fd509d9ee 100644
--- a/lib/logical-fields.c
+++ b/lib/logical-fields.c
@@ -145,6 +145,16 @@ ovn_init_symtab(struct shash *symtab)
                                     WR_CT_COMMIT);
     expr_symtab_add_subfield_scoped(symtab, "ct_mark.ecmp_reply_port", NULL,
                                     "ct_mark[16..31]", WR_CT_COMMIT);
+    expr_symtab_add_subfield_scoped(symtab, "ct_mark.skip_snat", NULL,
+                                    "ct_mark["
+                                    OVN_CT_STR(OVN_CT_LB_SKIP_SNAT_BIT)
+                                    "]",
+                                    WR_CT_COMMIT);
+    expr_symtab_add_subfield_scoped(symtab, "ct_mark.force_snat", NULL,
+                                    "ct_mark["
+                                    OVN_CT_STR(OVN_CT_LB_FORCE_SNAT_BIT)
+                                    "]",
+                                    WR_CT_COMMIT);
 
     expr_symtab_add_field_scoped(symtab, "ct_label", MFF_CT_LABEL, NULL,
                                  false, WR_CT_COMMIT);
@@ -167,6 +177,16 @@ ovn_init_symtab(struct shash *symtab)
                                     "ct_label[80..95]", WR_CT_COMMIT);
     expr_symtab_add_subfield_scoped(symtab, "ct_label.label", NULL,
                                     "ct_label[96..127]", WR_CT_COMMIT);
+    expr_symtab_add_subfield_scoped(symtab, "ct_label.skip_snat", NULL,
+                                    "ct_label["
+                                    OVN_CT_STR(OVN_CT_LB_SKIP_SNAT_BIT)
+                                    "]",
+                                    WR_CT_COMMIT);
+    expr_symtab_add_subfield_scoped(symtab, "ct_label.force_snat", NULL,
+                                    "ct_label["
+                                    OVN_CT_STR(OVN_CT_LB_FORCE_SNAT_BIT)
+                                    "]",
+                                    WR_CT_COMMIT);
 
     expr_symtab_add_field(symtab, "ct_state", MFF_CT_STATE, NULL, false);
 
diff --git a/northd/northd.c b/northd/northd.c
index a16e8a8a7..cb5b14181 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -6973,12 +6973,12 @@ build_lb_rules_pre_stateful(struct hmap *lflows, struct 
ovn_northd_lb *lb,
  *      match=(REGBIT_KNOWN_LB_SESSION == 1 && ct.new && ip4
  *             && REG_LB_AFF_BACKEND_IP4 == B1 && REG_LB_AFF_MATCH_PORT == BP1)
  *      action=(REG_NEXT_HOP_IPV4 = V; lb_action;
- *              ct_lb_mark(backends=B1:BP1);)
+ *              ct_lb_mark(backends=B1:BP1; ct_flag);)
  *   table=lr_in_dnat, priority=150
  *      match=(REGBIT_KNOWN_LB_SESSION == 1 && ct.new && ip4
  *             && REG_LB_AFF_BACKEND_IP4 == B2 && REG_LB_AFF_MATCH_PORT == BP2)
  *      action=(REG_NEXT_HOP_IPV4 = V; lb_action;
- *              ct_lb_mark(backends=B2:BP2);)
+ *              ct_lb_mark(backends=B2:BP2; ct_flag);)
  *
  * - load balancing affinity learn:
  *   table=lr_in_lb_aff_learn, priority=100
@@ -7036,6 +7036,13 @@ build_lb_affinity_lr_flows(struct hmap *lflows, struct 
ovn_northd_lb *lb,
     const char *reg_vip = ipv6 ? REG_NEXT_HOP_IPV6 : REG_NEXT_HOP_IPV4;
     const char *reg_backend =
         ipv6 ? REG_LB_L3_AFF_BACKEND_IP6 : REG_LB_AFF_BACKEND_IP4;
+    const char *ct_flag = NULL;
+    if (lb_action && !strcmp(lb_action, "flags.skip_snat_for_lb = 1; ")) {
+        ct_flag = "; skip_snat";
+    } else if (lb_action &&
+               !strcmp(lb_action, "flags.force_snat_for_lb = 1; ")) {
+        ct_flag = "; force_snat";
+    }
 
     /* Prepare common part of affinity LB and affinity learn action. */
     ds_put_format(&aff_action, "%s = %s; ", reg_vip, lb_vip->vip_str);
@@ -7099,6 +7106,10 @@ build_lb_affinity_lr_flows(struct hmap *lflows, struct 
ovn_northd_lb *lb,
             ds_put_cstr(&aff_action_learn, backend->ip_str);
         }
 
+        if (ct_flag) {
+            ds_put_cstr(&aff_action, ct_flag);
+        }
+
         ds_put_cstr(&aff_action, ");");
         ds_put_char(&aff_action_learn, '"');
 
@@ -10411,6 +10422,11 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip 
*lb_vip,
     bool reject = build_lb_vip_actions(lb_vip, vips_nb, action,
                                        lb->selection_fields, false,
                                        ct_lb_mark);
+    bool drop = !!strncmp(ds_cstr(action), "ct_lb", strlen("ct_lb"));
+    if (!drop) {
+        /* Remove the trailing ");". */
+        ds_truncate(action, action->length - 2);
+    }
 
     /* Higher priority rules are added for load-balancing in DNAT
      * table.  For every match (on a VIP[:port]), we add two flows.
@@ -10427,8 +10443,9 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip 
*lb_vip,
     }
 
     if (lb->skip_snat) {
-        skip_snat_new_action = xasprintf("flags.skip_snat_for_lb = 1; %s",
-                                         ds_cstr(action));
+        skip_snat_new_action = xasprintf("flags.skip_snat_for_lb = 1; %s%s",
+                                         ds_cstr(action),
+                                         drop ? "" : "; skip_snat);");
         skip_snat_est_action = xasprintf("flags.skip_snat_for_lb = 1; "
                                          "next;");
     }
@@ -10561,8 +10578,9 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip 
*lb_vip,
             skip_snat_new_action, est_match,
             skip_snat_est_action, lflows, prio, meter_groups);
 
-    char *new_actions = xasprintf("flags.force_snat_for_lb = 1; %s",
-                                  ds_cstr(action));
+    char *new_actions = xasprintf("flags.force_snat_for_lb = 1; %s%s",
+                                  ds_cstr(action),
+                                  drop ? "" : "; force_snat);");
     build_gw_lrouter_nat_flows_for_lb(lb, gw_router_force_snat,
             n_gw_router_force_snat, reject, new_match,
             new_actions, est_match,
@@ -10577,6 +10595,10 @@ build_lrouter_nat_flows_for_lb(struct ovn_lb_vip 
*lb_vip,
                                lb_aff_force_snat_router,
                                n_lb_aff_force_snat_router);
 
+    if (!drop) {
+        ds_put_cstr(action, ");");
+    }
+
     build_gw_lrouter_nat_flows_for_lb(lb, gw_router, n_gw_router,
             reject, new_match, ds_cstr(action), est_match,
             "next;", lflows, prio, meter_groups);
@@ -14142,7 +14164,7 @@ 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).
+    /* 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.
@@ -14155,10 +14177,29 @@ build_lrouter_nat_defrag_and_lb(struct ovn_datapath 
*od, struct hmap *lflows,
      * related traffic such as an ICMP Port Unreachable through
      * that's generated from a non-listening UDP port.  */
     if (od->has_lb_vip) {
+        ds_clear(match);
+        const char *ct_flag_reg = ct_lb_mark ? "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;");
+
+        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; 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;");
+
+        ds_clear(match);
     }
 
     /* 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 4d455e0f3..058cbf71a 100644
--- a/northd/ovn-northd.8.xml
+++ b/northd/ovn-northd.8.xml
@@ -3495,7 +3495,9 @@ icmp6 {
             For the related traffic, a priority 50 flow that matches
             <code>ct.rel &amp;&amp; !ct.est &amp;&amp; !ct.new </code>
             with an action of <code>ct_commit_nat;</code>, if the router
-            has load balancer assigned to it.
+            has load balancer assigned to it. Along with two priority 70 flows
+            that match <code>skip_snat</code> and <code>force_snat</code>
+            flags.
         </p>
       </li>
     </ul>
diff --git a/ovn-sb.xml b/ovn-sb.xml
index 4f485b860..d5287e3c2 100644
--- a/ovn-sb.xml
+++ b/ovn-sb.xml
@@ -2033,7 +2033,7 @@
         </dd>
 
         <dt><code>ct_lb;</code></dt>
-        <dt><code>ct_lb(backends=<var>ip</var>[:<var>port</var>][,...][; 
hash_fields=<var>field1</var>,<var>field2</var>,...]);</code></dt>
+        <dt><code>ct_lb(backends=<var>ip</var>[:<var>port</var>][,...][; 
hash_fields=<var>field1</var>,<var>field2</var>,...][; ct_flag]);</code></dt>
         <dd>
           <p>
             With arguments, <code>ct_lb</code> commits the packet
@@ -2045,7 +2045,10 @@
             <code>dp_hash</code> is used as the OpenFlow group selection
             method, but if <code>hash_fields</code> is specified,
             <code>hash</code> is used as the selection method, and the fields
-            listed are used as the hash fields.
+            listed are used as the hash fields. The <code>ct_flag</code>
+            field represents one of supported flag: <code>skip_snat</code> or
+            <code>force_snat</code>, this flag will be stored in
+            <code>ct_label</code> register.
           </p>
           <p>
             Without arguments, <code>ct_lb</code> sends the packet to the
@@ -2067,7 +2070,7 @@
         </dd>
 
         <dt><code>ct_lb_mark;</code></dt>
-        <dt><code>ct_lb_mark(backends=<var>ip</var>[:<var>port</var>][,...][; 
hash_fields=<var>field1</var>,<var>field2</var>,...]);</code></dt>
+        <dt><code>ct_lb_mark(backends=<var>ip</var>[:<var>port</var>][,...][; 
hash_fields=<var>field1</var>,<var>field2</var>,...][; ct_flag]);</code></dt>
         <dd>
           <p>
               Same as <code>ct_lb</code>, except that it internally uses 
ct_mark
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 7cc53e367..cf06ce7f9 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -3679,6 +3679,8 @@ AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
   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=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  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;)
 ])
 
 AT_CHECK([grep "lr_out_undnat" lr0flows | sed 's/table=./table=?/' | sort], 
[0], [dnl
@@ -3714,9 +3716,11 @@ 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);)
-  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);)
+  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=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  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;)
 ])
 
 AT_CHECK([grep "lr_out_snat" lr0flows | sed 's/table=./table=?/' | sort], [0], 
[dnl
@@ -3762,9 +3766,11 @@ 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);)
-  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);)
+  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=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  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;)
 ])
 
 AT_CHECK([grep "lr_out_snat" lr0flows | sed 's/table=./table=?/' | sort], [0], 
[dnl
@@ -3824,9 +3830,11 @@ 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);)
-  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);)
+  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=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  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;)
 ])
 
 AT_CHECK([grep "lr_out_snat" lr0flows | sed 's/table=./table=?/' | sort], [0], 
[dnl
@@ -3871,7 +3879,8 @@ AT_CHECK([grep "lr_in_defrag" lr0flows | sort], [0], [dnl
 
 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);)
+  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=70   , match=(ct.rel && !ct.est && 
!ct.new && ct_mark.skip_snat == 1), action=(flags.skip_snat_for_lb = 1; 
ct_commit_nat;)
 ])
 
 AT_CHECK([grep "lr_out_snat" lr0flows | grep skip_snat_for_lb | sed 
's/table=./table=?/' | sort], [0], [dnl
@@ -5135,6 +5144,8 @@ AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
   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=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  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;)
 ])
 
 AT_CHECK([grep "lr_out_chk_dnat_local" lr0flows | sed 's/table=./table=?/' | 
sort], [0], [dnl
@@ -5206,6 +5217,8 @@ AT_CHECK([grep "lr_in_dnat" lr0flows | sort], [0], [dnl
   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=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  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;)
 ])
 
 AT_CHECK([grep "lr_out_chk_dnat_local" lr0flows | sed 's/table=./table=?/' | 
sort], [0], [dnl
@@ -5261,14 +5274,16 @@ 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);)
+  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);)
-  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);)
-  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);)
+  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=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  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;)
 ])
 
 AT_CHECK([grep "lr_out_chk_dnat_local" lr0flows | sed 's/table=./table=?/' | 
sort], [0], [dnl
@@ -5327,16 +5342,18 @@ 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);)
+  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);)
-  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);)
-  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);)
-  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);)
+  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=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  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;)
 ])
 
 AT_CHECK([grep "lr_out_chk_dnat_local" lr0flows | sed 's/table=./table=?/' | 
sort], [0], [dnl
@@ -5406,18 +5423,20 @@ 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);)
+  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);)
-  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);)
-  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);)
-  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);)
-  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);)
+  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=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  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;)
 ])
 
 AT_CHECK([grep "lr_out_chk_dnat_local" lr0flows | sed 's/table=./table=?/' | 
sort], [0], [dnl
@@ -5476,9 +5495,11 @@ 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);)
-  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);)
+  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=50   , match=(ct.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  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;)
 ])
 
 AT_CHECK([grep "lr_out_chk_dnat_local" lr0flows | sed 's/table=./table=?/' | 
sort], [0], [dnl
@@ -8053,6 +8074,8 @@ AT_CHECK([grep "lr_in_dnat " R1flows | sort], [0], [dnl
   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.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  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;)
 ])
 AT_CHECK([grep "lr_in_lb_aff_learn" R1flows | sort], [0], [dnl
   table=8 (lr_in_lb_aff_learn ), priority=0    , match=(1), action=(next;)
@@ -8069,10 +8092,12 @@ 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);)
-  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);)
-  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);)
+  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=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.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  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;)
 ])
 
 check ovn-nbctl remove load_balancer lb0 options skip_snat
@@ -8086,10 +8111,12 @@ 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);)
-  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);)
-  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);)
+  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=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.rel && !ct.est && 
!ct.new), action=(ct_commit_nat;)
+  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;)
 ])
 
 AT_CLEANUP
diff --git a/tests/ovn.at b/tests/ovn.at
index f3bd53242..2ac002804 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -309,12 +309,16 @@ ct_label = NXM_NX_CT_LABEL
 ct_label.blocked = ct_label[0]
 ct_label.ecmp_reply_eth = ct_label[32..79]
 ct_label.ecmp_reply_port = ct_label[80..95]
+ct_label.force_snat = ct_label[3]
 ct_label.label = ct_label[96..127]
 ct_label.natted = ct_label[1]
+ct_label.skip_snat = ct_label[2]
 ct_mark = NXM_NX_CT_MARK
 ct_mark.blocked = ct_mark[0]
 ct_mark.ecmp_reply_port = ct_mark[16..31]
+ct_mark.force_snat = ct_mark[3]
 ct_mark.natted = ct_mark[1]
+ct_mark.skip_snat = ct_mark[2]
 ct_state = NXM_NX_CT_STATE
 ]])
 AT_CLEANUP
@@ -1123,15 +1127,23 @@ ct_lb(backends=192.168.1.2:80,192.168.1.3:80);
     encodes as group:1
     uses group: id(1), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
     has prereqs ip
+ct_lb(backends=192.168.1.2:80,192.168.1.3:80; skip_snat);
+    encodes as group:2
+    uses group: id(2), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label,set_field:4/4->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label,set_field:4/4->ct_label)))
+    has prereqs ip
+ct_lb(backends=192.168.1.2:80,192.168.1.3:80; force_snat);
+    encodes as group:3
+    uses group: id(3), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label,set_field:8/8->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label,set_field:8/8->ct_label)))
+    has prereqs ip
 ct_lb(backends=192.168.1.2, 192.168.1.3, );
     formats as ct_lb(backends=192.168.1.2,192.168.1.3);
-    encodes as group:2
-    uses group: id(2), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
+    encodes as group:4
+    uses group: id(4), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
     has prereqs ip
 ct_lb(backends=fd0f::2, fd0f::3, );
     formats as ct_lb(backends=fd0f::2,fd0f::3);
-    encodes as group:3
-    uses group: id(3), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
+    encodes as group:5
+    uses group: id(5), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
     has prereqs ip
 
 ct_lb(backends=192.168.1.2:);
@@ -1146,30 +1158,59 @@ ct_lb(backends=[192.168.1.2]);
 ct_lb(backends=192.168.1.2:80,192.168.1.3:80; 
hash_fields=eth_src,eth_dst,ip_src);
     Syntax error at `eth_src' invalid hash_fields.
 ct_lb(backends=192.168.1.2:80,192.168.1.3:80; 
hash_fields="eth_src,eth_dst,ip_src");
-    encodes as group:4
-    uses group: id(4), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
+    encodes as group:6
+    uses group: id(6), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
     has prereqs ip
 ct_lb(backends=fd0f::2,fd0f::3; 
hash_fields="eth_src,eth_dst,ip_src,ip_dst,tp_src,tp_dst");
-    encodes as group:5
-    uses group: id(5), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,tp_src,tp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
+    encodes as group:7
+    uses group: id(7), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,tp_src,tp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
     has prereqs ip
 ct_lb(backends=fd0f::2,fd0f::3; 
hash_fields="eth_src,eth_dst,ip_src,ip_dst,tcp_src,tcp_dst");
-    encodes as group:6
-    uses group: id(6), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,tcp_src,tcp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
+    encodes as group:8
+    uses group: id(8), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,tcp_src,tcp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
     has prereqs ip
 ct_lb(backends=fd0f::2,fd0f::3; 
hash_fields="eth_src,eth_dst,ip_src,ip_dst,udp_src,udp_dst");
-    encodes as group:7
-    uses group: id(7), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,udp_src,udp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
+    encodes as group:9
+    uses group: id(9), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,udp_src,udp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
     has prereqs ip
 ct_lb(backends=fd0f::2,fd0f::3; 
hash_fields="eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst");
-    encodes as group:8
-    uses group: id(8), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
+    encodes as group:10
+    uses group: id(10), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label)))
+    has prereqs ip
+ct_lb(backends=fd0f::2,fd0f::3; 
hash_fields="eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst"; skip_snat);
+    encodes as group:11
+    uses group: id(11), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label,set_field:4/4->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label,set_field:4/4->ct_label)))
+    has prereqs ip
+ct_lb(backends=fd0f::2,fd0f::3; 
hash_fields="eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst"; force_snat);
+    encodes as group:12
+    uses group: id(12), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label,set_field:8/8->ct_label)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_label,set_field:8/8->ct_label)))
     has prereqs ip
 
 ct_lb_mark(backends=192.168.1.2:80,192.168.1.3:80);
-    encodes as group:9
-    uses group: id(9), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark)))
+    encodes as group:13
+    uses group: id(13), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark)))
+    has prereqs ip
+ct_lb_mark(backends=192.168.1.2:80,192.168.1.3:80; skip_snat);
+    encodes as group:14
+    uses group: id(14), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark,set_field:4/4->ct_mark)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark,set_field:4/4->ct_mark)))
+    has prereqs ip
+ct_lb_mark(backends=192.168.1.2:80,192.168.1.3:80; force_snat);
+    encodes as group:15
+    uses group: id(15), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark,set_field:8/8->ct_mark)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark,set_field:8/8->ct_mark)))
+    has prereqs ip
+ct_lb_mark(backends=192.168.1.2:80,192.168.1.3:80; 
hash_fields="eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst");
+    encodes as group:16
+    uses group: id(16), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark)))
     has prereqs ip
+ct_lb_mark(backends=192.168.1.2:80,192.168.1.3:80; 
hash_fields="eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst"; skip_snat);
+    encodes as group:17
+    uses group: id(17), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark,set_field:4/4->ct_mark)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark,set_field:4/4->ct_mark)))
+    has prereqs ip
+ct_lb_mark(backends=192.168.1.2:80,192.168.1.3:80; 
hash_fields="eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst"; force_snat);
+    encodes as group:18
+    uses group: id(18), 
name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,sctp_src,sctp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark,set_field:8/8->ct_mark)),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15],exec(set_field:2/2->ct_mark,set_field:8/8->ct_mark)))
+    has prereqs ip
+
 # ct_next
 ct_next;
     encodes as ct(table=19,zone=NXM_NX_REG13[0..15])
@@ -1927,13 +1968,13 @@ handle_svc_check(reg0);
 # select
 reg9[16..31] = select(1=50, 2=100, 3, );
     formats as reg9[16..31] = select(1=50, 2=100, 3=100);
-    encodes as group:10
-    uses group: id(10), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:50,actions=load:1->xreg4[16..31],resubmit(,19),bucket=bucket_id=1,weight:100,actions=load:2->xreg4[16..31],resubmit(,19),bucket=bucket_id=2,weight:100,actions=load:3->xreg4[16..31],resubmit(,19))
+    encodes as group:19
+    uses group: id(19), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:50,actions=load:1->xreg4[16..31],resubmit(,19),bucket=bucket_id=1,weight:100,actions=load:2->xreg4[16..31],resubmit(,19),bucket=bucket_id=2,weight:100,actions=load:3->xreg4[16..31],resubmit(,19))
 
 reg0 = select(1, 2);
     formats as reg0 = select(1=100, 2=100);
-    encodes as group:11
-    uses group: id(11), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=load:1->xxreg0[96..127],resubmit(,19),bucket=bucket_id=1,weight:100,actions=load:2->xxreg0[96..127],resubmit(,19))
+    encodes as group:20
+    uses group: id(20), 
name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=load:1->xxreg0[96..127],resubmit(,19),bucket=bucket_id=1,weight:100,actions=load:2->xxreg0[96..127],resubmit(,19))
 
 reg0 = select(1=, 2);
     Syntax error at `,' expecting weight.
@@ -1950,12 +1991,12 @@ reg0[0..14] = select(1, 2, 3);
 
 fwd_group(liveness=true, childports="eth0", "lsp1");
     formats as fwd_group(liveness="true", childports="eth0", "lsp1");
-    encodes as group:12
-    uses group: id(12), 
name(type=select,selection_method=dp_hash,bucket=watch_port:5,load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=watch_port:17,load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
+    encodes as group:21
+    uses group: id(21), 
name(type=select,selection_method=dp_hash,bucket=watch_port:5,load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=watch_port:17,load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
 
 fwd_group(childports="eth0", "lsp1");
-    encodes as group:13
-    uses group: id(13), 
name(type=select,selection_method=dp_hash,bucket=load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
+    encodes as group:22
+    uses group: id(22), 
name(type=select,selection_method=dp_hash,bucket=load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
 
 fwd_group(childports=eth0);
     Syntax error at `eth0' expecting logical switch port.
@@ -1964,8 +2005,8 @@ fwd_group();
     Syntax error at `)' expecting `;'.
 
 fwd_group(childports="eth0", "lsp1");
-    encodes as group:13
-    uses group: id(13), 
name(type=select,selection_method=dp_hash,bucket=load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
+    encodes as group:22
+    uses group: id(22), 
name(type=select,selection_method=dp_hash,bucket=load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
 
 fwd_group(liveness=xyzzy, childports="eth0", "lsp1");
     Syntax error at `xyzzy' expecting true or false.
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index e3325acae..99ad14aa5 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -2197,6 +2197,7 @@ grep 'nat(dst=192.168.2.2:80)'])
 OVS_START_L7([foo1], [http])
 OVS_START_L7([bar1], [http])
 
+check ovs-appctl dpctl/flush-conntrack
 dnl Should work with the virtual IP address through NAT
 for i in `seq 1 20`; do
     echo Request $i
@@ -2210,6 +2211,7 @@ 
tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(sr
 
tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
 ])
 
+check ovs-appctl dpctl/flush-conntrack
 dnl Test load-balancing that includes L4 ports in NAT.
 for i in `seq 1 20`; do
     echo Request $i
@@ -2253,6 +2255,7 @@ ovn-sbctl dump-flows R2
 OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-flows br-int table=43 | \
 grep 'nat(src=20.0.0.2)'])
 
+check ovs-appctl dpctl/flush-conntrack
 dnl Test load-balancing that includes L4 ports in NAT.
 for i in `seq 1 20`; do
     echo Request $i
@@ -2262,8 +2265,8 @@ done
 dnl Each server should have at least one connection.
 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) |
 sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
-tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=10,protoinfo=(state=<cleared>)
+tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=10,protoinfo=(state=<cleared>)
 ])
 
 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.2) |
@@ -2293,6 +2296,7 @@ grep 'nat(src=20.0.0.2)'])
 
 rm -f wget*.log
 
+check ovs-appctl dpctl/flush-conntrack
 dnl Test load-balancing that includes L4 ports in NAT.
 for i in `seq 1 20`; do
     echo Request $i
@@ -2302,8 +2306,8 @@ done
 dnl Each server should have at least one connection.
 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) |
 sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
-tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=10,protoinfo=(state=<cleared>)
+tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=10,protoinfo=(state=<cleared>)
 ])
 
 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.2) |
@@ -2731,8 +2735,8 @@ done
 dnl Each server should have at least one connection.
 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) |
 sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
-tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=10,protoinfo=(state=<cleared>)
+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=10,protoinfo=(state=<cleared>)
 ])
 
 dnl Force SNAT should have worked.
@@ -2904,8 +2908,8 @@ done
 dnl Each server should have at least one connection.
 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | grep -v fe80 |
 sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
-tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=10,protoinfo=(state=<cleared>)
+tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=10,protoinfo=(state=<cleared>)
 ])
 
 dnl Force SNAT should have worked.
@@ -3120,13 +3124,13 @@ done
 dnl Each server should have at least one connection.
 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) |
 sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
-tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=10,protoinfo=(state=<cleared>)
+tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=10,protoinfo=(state=<cleared>)
 ])
 AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | grep -v fe80 |
 sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
-tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=2,protoinfo=(state=<cleared>)
+tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd11::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=10,protoinfo=(state=<cleared>)
+tcp,orig=(src=fd72::3,dst=fd30::1,sport=<cleared>,dport=<cleared>),reply=(src=fd12::2,dst=fd72::3,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=10,protoinfo=(state=<cleared>)
 ])
 
 dnl Force SNAT should have worked.
@@ -9352,6 +9356,29 @@ 
udp,orig=(src=192.168.10.10,dst=192.168.20.20,sport=<cleared>,dport=<cleared>),r
 check ovn-nbctl lr-lb-del lr
 check ovn-nbctl lb-del lb0
 
+AS_BOX([ICMP related force SNAT])
+check ovn-nbctl lb-add lb0 192.168.20.20 192.168.20.10
+check ovn-nbctl lr-lb-add lr lb0
+
+check ovn-nbctl --wait=sb set logical_router lr 
options:lb_force_snat_ip="router_ip"
+
+# Change the expected packets on server to be with source IP from router
+server_udp_expected=00000000201000000000200008004500001c000040000911c875c0a8\
+1401c0a8140a0001000200080000
+icmp_expected=000000002010000000002000080045000038011f0000fe01124ac0a81401c0\
+a8140a0304f778000005784500001c000040000911c875c0a8140ac0a814010002000100080000
+
+# Server should respond to the router IP
+server_udp=00000000200000000000201008004500001C000040000A11C775C0A8140AC0A8\
+14010002000100080000
+
+test_related_traffic
+
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.20.20) | \
+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
+udp,orig=(src=192.168.10.10,dst=192.168.20.20,sport=<cleared>,dport=<cleared>),reply=(src=192.168.20.10,dst=192.168.10.10,sport=<cleared>,dport=<cleared>),zone=<cleared>,mark=10
+])
+
 OVS_APP_EXIT_AND_WAIT([ovn-controller])
 
 as ovn-sb
-- 
2.38.1

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

Reply via email to