Acked-by: Mark Michelson <[email protected]>

On 12/7/22 10:34, Ales Musil wrote:
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

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

Reply via email to