diff mbox series

[ovs-dev,RFC,2/2] northd: Commit all traffic when there is stateful NAT/LB.

Message ID 20241008101155.331151-3-amusil@redhat.com
State Superseded
Headers show
Series Commit all traffic for stateful NAT/LB | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test fail github build: failed
ovsrobot/github-robot-_ovn-kubernetes fail github build: failed

Commit Message

Ales Musil Oct. 8, 2024, 10:11 a.m. UTC
Commit all traffic that is not already commit by either NAT or LB. This
ensures that the traffic is tracked, and we don't erroneously commit
reply traffic, or reply traffic is not marked as invalid.

To achieve the commit we need to perform lookup on every packet
that goes through LR pipeline whenever there is stateful NAT.

The SNAT lookup requires additional flag as the unSNAT is happening
in ingress pipeline and at that point we need to know if the packet
is reply or not. This is not required for DNAT, because unDNAT stage
happens in egress.

Signed-off-by: Ales Musil <amusil@redhat.com>
---
There is one failing system test with userspace datapath, that's due
to the recirculation limit that is being hit due to additional
lookups.
---
 include/ovn/logical-fields.h |   4 ++
 lib/logical-fields.c         |   4 ++
 northd/northd.c              |  76 ++++++++++++----------
 tests/ovn-northd.at          | 118 ++++++++++++++++++++++++++---------
 tests/system-ovn.at          |  31 ++++++---
 5 files changed, 158 insertions(+), 75 deletions(-)

Comments

Ales Musil Oct. 9, 2024, 5:44 a.m. UTC | #1
On Tue, Oct 8, 2024 at 12:12 PM Ales Musil <amusil@redhat.com> wrote:

> Commit all traffic that is not already commit by either NAT or LB. This
> ensures that the traffic is tracked, and we don't erroneously commit
> reply traffic, or reply traffic is not marked as invalid.
>
> To achieve the commit we need to perform lookup on every packet
> that goes through LR pipeline whenever there is stateful NAT.
>
> The SNAT lookup requires additional flag as the unSNAT is happening
> in ingress pipeline and at that point we need to know if the packet
> is reply or not. This is not required for DNAT, because unDNAT stage
> happens in egress.
>
> Signed-off-by: Ales Musil <amusil@redhat.com>
> ---
> There is one failing system test with userspace datapath, that's due
> to the recirculation limit that is being hit due to additional
> lookups.
> ---
>  include/ovn/logical-fields.h |   4 ++
>  lib/logical-fields.c         |   4 ++
>  northd/northd.c              |  76 ++++++++++++----------
>  tests/ovn-northd.at          | 118 ++++++++++++++++++++++++++---------
>  tests/system-ovn.at          |  31 ++++++---
>  5 files changed, 158 insertions(+), 75 deletions(-)
>
> diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
> index d6c4a9b6b..cc1f50ff2 100644
> --- a/include/ovn/logical-fields.h
> +++ b/include/ovn/logical-fields.h
> @@ -82,6 +82,7 @@ enum mff_log_flags_bits {
>      MLF_LOCALNET_BIT = 15,
>      MLF_RX_FROM_TUNNEL_BIT = 16,
>      MLF_ICMP_SNAT_BIT = 17,
> +    MLF_WITHOUT_UNSNAT_BIT = 18,
>  };
>
>  /* MFF_LOG_FLAGS_REG flag assignments */
> @@ -137,6 +138,9 @@ enum mff_log_flags {
>      MLF_RX_FROM_TUNNEL = (1 << MLF_RX_FROM_TUNNEL_BIT),
>
>      MLF_ICMP_SNAT = (1 << MLF_ICMP_SNAT_BIT),
> +
> +    /* Indicate that the packet didn't go through unSNAT. */
> +    MLF_WITHOUT_UNSNAT = (1 << MLF_WITHOUT_UNSNAT_BIT),
>  };
>
>  /* OVN logical fields
> diff --git a/lib/logical-fields.c b/lib/logical-fields.c
> index 5a8b53f2b..c63e19897 100644
> --- a/lib/logical-fields.c
> +++ b/lib/logical-fields.c
> @@ -139,6 +139,10 @@ ovn_init_symtab(struct shash *symtab)
>                               flags_str);
>      snprintf(flags_str, sizeof flags_str, "flags[%d]",
> MLF_RX_FROM_TUNNEL_BIT);
>      expr_symtab_add_subfield(symtab, "flags.tunnel_rx", NULL, flags_str);
> +    snprintf(flags_str, sizeof flags_str, "flags[%d]",
> +             MLF_WITHOUT_UNSNAT_BIT);
> +    expr_symtab_add_subfield(symtab, "flags.without_unsnat", NULL,
> +                             flags_str);
>
>      /* Connection tracking state. */
>      expr_symtab_add_field_scoped(symtab, "ct_mark", MFF_CT_MARK, NULL,
> false,
> diff --git a/northd/northd.c b/northd/northd.c
> index 0364dd766..a42057e45 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -15987,8 +15987,7 @@ build_lrouter_out_snat_flow(struct lflow_table
> *lflows,
>                              struct ds *actions, bool distributed_nat,
>                              struct eth_addr mac, int cidr_bits, bool
> is_v6,
>                              struct ovn_port *l3dgw_port,
> -                            struct lflow_ref *lflow_ref,
> -                            const struct chassis_features *features)
> +                            struct lflow_ref *lflow_ref)
>  {
>      if (!(nat_entry->type == SNAT || nat_entry->type == DNAT_AND_SNAT)) {
>          return;
> @@ -16019,34 +16018,6 @@ build_lrouter_out_snat_flow(struct lflow_table
> *lflows,
>                              priority, ds_cstr(match),
>                              ds_cstr(actions), &nat->header_,
>                              lflow_ref);
> -
> -    /* For the SNAT networks, we need to make sure that connections are
> -     * properly tracked so we can decide whether to perform SNAT on
> traffic
> -     * exiting the network. */
> -    if (features->ct_commit_to_zone && features->ct_next_zone &&
> -        nat_entry->type == SNAT && !od->is_gw_router) {
> -        /* For traffic that comes from SNAT network, initiate CT state
> before
> -         * entering S_ROUTER_OUT_SNAT to allow matching on various CT
> states.
> -         */
> -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 70,
> -                      ds_cstr(match), "ct_next(snat);",
> -                      lflow_ref);
> -
> -        build_lrouter_out_snat_match(lflows, od, nat, match,
> -                                     distributed_nat, cidr_bits, is_v6,
> -                                     l3dgw_port, lflow_ref, true);
> -
> -        /* New traffic that goes into SNAT network is committed to CT to
> avoid
> -         * SNAT-ing replies.*/
> -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, priority,
> -                      ds_cstr(match), "ct_snat;",
> -                      lflow_ref);
> -
> -        ds_put_cstr(match, " && ct.new");
> -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_SNAT, priority,
> -                      ds_cstr(match), "ct_commit_to_zone(snat);",
> -                      lflow_ref);
> -    }
>  }
>
>  static void
> @@ -16439,9 +16410,6 @@ build_lrouter_nat_defrag_and_lb(
>          ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 50,
>                        "ip", "flags.loopback = 1; ct_dnat;",
>                        lflow_ref);
> -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 50,
> -                      "ip && ct.new", "ct_commit { } ; next; ",
> -                      lflow_ref);
>      }
>
>      /* NAT rules are only valid on Gateway routers and routers with
> @@ -16459,6 +16427,9 @@ build_lrouter_nat_defrag_and_lb(
>          !lport_addresses_is_empty(&lrnat_rec->dnat_force_snat_addrs);
>      bool lb_force_snat_ip =
>          !lport_addresses_is_empty(&lrnat_rec->lb_force_snat_addrs);
> +    bool stateful_dnat = lr_stateful_rec->has_lb_vip;
> +    bool stateful_snat = (dnat_force_snat_ip || lb_force_snat_ip ||
> +                          lrnat_rec->lb_force_snat_router_ip);
>
>      for (size_t i = 0; i < lrnat_rec->n_nat_entries; i++) {
>          struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i];
> @@ -16477,6 +16448,21 @@ build_lrouter_nat_defrag_and_lb(
>              continue;
>          }
>
> +        if (!stateless) {
> +            switch (nat_entry->type) {
> +            case DNAT:
> +                stateful_dnat = true;
> +                break;
> +            case SNAT:
> +                stateful_snat = true;
> +                break;
> +            case DNAT_AND_SNAT:
> +                stateful_snat = true;
> +                stateful_dnat = true;
> +                break;
> +            }
> +        }
> +
>          /* S_ROUTER_IN_UNSNAT
>           * Ingress UNSNAT table: It is for already established
> connections'
>           * reverse traffic. i.e., SNAT has already been done in egress
> @@ -16599,7 +16585,7 @@ build_lrouter_nat_defrag_and_lb(
>          } else {
>              build_lrouter_out_snat_flow(lflows, od, nat_entry, match,
> actions,
>                                          distributed_nat, mac, cidr_bits,
> is_v6,
> -                                        l3dgw_port, lflow_ref, features);
> +                                        l3dgw_port, lflow_ref);
>          }
>
>          /* S_ROUTER_IN_ADMISSION - S_ROUTER_IN_IP_INPUT */
> @@ -16689,6 +16675,28 @@ build_lrouter_nat_defrag_and_lb(
>          }
>      }
>
> +
> +    bool can_commit = features->ct_commit_to_zone &&
> features->ct_next_zone;
> +    if (can_commit && stateful_dnat) {
> +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 10,
> +                      "ip && (!ct.trk || !ct.rpl)",
> +                      "ct_next(dnat);", lflow_ref);
> +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 10,
> +                      "ip && ct.new", "ct_commit_to_zone(dnat);",
> lflow_ref);
> +    }
> +
> +    if (can_commit && stateful_snat) {
> +        ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 10,
> +                      "ip", "flags.without_unsnat = 1; next;", lflow_ref);
> +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 10,
> +                      "ip && (!ct.trk || !ct.rpl) && "
> +                      "flags.without_unsnat == 1", "ct_next(snat);",
> +                      lflow_ref);
> +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 10,
> +                      "ip && ct.new && flags.without_unsnat == 1",
> +                      "ct_commit_to_zone(snat);", lflow_ref);
> +    }
> +
>      if (use_common_zone && od->nbr->n_nat) {
>          ds_clear(match);
>          ds_put_cstr(match, "ip && ct_mark.natted == 1");
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index d6a8c4640..96e28a54a 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -1181,18 +1181,18 @@ AT_CAPTURE_FILE([crflows])
>
>  AT_CHECK([grep -e "lr_out_snat" drflows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
> 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ip4.src == $allowed_range), action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
> 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.16.1.1);)
>  ])
>
>  AT_CHECK([grep -e "lr_out_post_snat" drflows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_snat   ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
> 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ip4.src == $allowed_range && ct.new), action=(ct_commit_to_zone(snat);)
>  ])
>
>  AT_CHECK([grep -e "lr_out_snat" crflows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
>    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src ==
> 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.16.1.1);)
>  ])
> @@ -1220,19 +1220,19 @@ AT_CAPTURE_FILE([crflows2])
>
>  AT_CHECK([grep -e "lr_out_snat" drflows2 | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
> 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1")),
> action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
> 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
>    table=??(lr_out_snat        ), priority=163  , match=(ip && ip4.src ==
> 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ip4.dst == $disallowed_range), action=(next;)
>  ])
>
>  AT_CHECK([grep -e "lr_out_post_snat" drflows2 | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_snat   ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
> 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ct.new), action=(ct_commit_to_zone(snat);)
>  ])
>
>  AT_CHECK([grep -e "lr_out_snat" crflows2 | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
>    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src ==
> 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
>    table=??(lr_out_snat        ), priority=35   , match=(ip && ip4.src ==
> 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
> @@ -1259,6 +1259,7 @@ AT_CAPTURE_FILE([crflows2])
>
>  AT_CHECK([grep -e "lr_out_snat" drflows3 | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
> 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.16.1.2);)
>  ])
> @@ -1269,6 +1270,7 @@ AT_CHECK([grep -e "lr_out_post_snat" drflows3 |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep -e "lr_out_snat" crflows3 | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
>    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src ==
> 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.16.1.2);)
>  ])
> @@ -1294,6 +1296,7 @@ AT_CAPTURE_FILE([crflows2])
>
>  AT_CHECK([grep -e "lr_out_snat" drflows4 | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
> 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
>    table=??(lr_out_snat        ), priority=163  , match=(ip && ip4.src ==
> 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ip4.dst == $disallowed_range), action=(next;)
> @@ -1301,6 +1304,7 @@ AT_CHECK([grep -e "lr_out_snat" drflows4 |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep -e "lr_out_snat" crflows4 | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
>    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src ==
> 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
>    table=??(lr_out_snat        ), priority=35   , match=(ip && ip4.src ==
> 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
> @@ -1663,6 +1667,7 @@ AT_CAPTURE_FILE([sbflows])
>  # dnat_and_snat or snat entry.
>  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
> 192.168.2.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
> 192.168.2.4), action=(ct_snat;)
>  ])
> @@ -1693,6 +1698,7 @@ AT_CAPTURE_FILE([sbflows])
>  # dnat_and_snat or snat entry.
>  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
> 192.168.2.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
> 192.168.2.4), action=(ct_snat;)
>  ])
> @@ -1801,6 +1807,7 @@ ovn-nbctl --wait=sb sync
>
>  AT_CHECK([ovn-sbctl lflow-list lr0 | grep lr_in_unsnat |
> ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 && ip4.dst ==
> 192.168.2.3), action=(ct_snat;)
>  ])
>
> @@ -4277,12 +4284,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.100), action=(ct_dnat;)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(ct_lb_mark(backends=10.0.0.4:8080);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
> action=(ct_lb_mark(backends=10.0.0.40:8080);)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel
> && !ct.new && ct_mark.natted), action=(next;)
> @@ -4302,18 +4311,21 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 && ip4.dst ==
> 20.0.0.4), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(ip6 && ip6.dst ==
> aef0::4), action=(ct_snat;)
>  ])
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.100), action=(ct_dnat;)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
> force_snat);)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel
> && !ct.new && ct_mark.natted), action=(next;)
> @@ -4326,6 +4338,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=100  ,
> match=(flags.force_snat_for_lb == 1 && ip4), action=(ct_snat(20.0.0.4);)
>    table=??(lr_out_snat        ), priority=100  ,
> match=(flags.force_snat_for_lb == 1 && ip6), action=(ct_snat(aef0::4);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> @@ -4339,7 +4352,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  check ovn-nbctl --wait=sb set logical_router lr0
> options:lb_force_snat_ip="router_ip"
> @@ -4352,6 +4365,7 @@ AT_CHECK([grep "lr_in_ip_input" lr0flows | grep
> "priority=60" | sort], [0], [dnl
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
> @@ -4359,12 +4373,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.100), action=(ct_dnat;)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
> force_snat);)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel
> && !ct.new && ct_mark.natted), action=(next;)
> @@ -4377,6 +4393,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
> action=(ct_snat(172.168.0.100);)
>    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
> action=(ct_snat(10.0.0.1);)
>    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
> action=(ct_snat(20.0.0.1);)
> @@ -4391,7 +4408,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  check ovn-nbctl --wait=sb remove logical_router lr0 options chassis
> @@ -4416,6 +4433,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
> @@ -4424,12 +4442,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.100), action=(ct_dnat;)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
> force_snat);)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel
> && !ct.new && ct_mark.natted), action=(next;)
> @@ -4442,6 +4462,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
> action=(ct_snat(172.168.0.100);)
>    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
> action=(ct_snat(10.0.0.1);)
>    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
> action=(ct_snat(20.0.0.1);)
> @@ -4457,7 +4478,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  check ovn-nbctl --wait=sb lb-add lb2 10.0.0.20:80 10.0.0.40:8080
> @@ -4468,6 +4489,7 @@ ovn-sbctl dump-flows lr0 > lr0flows
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
> @@ -4476,6 +4498,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.100), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.20), action=(ct_dnat;)
>  ])
> @@ -4498,7 +4521,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CLEANUP
> @@ -5744,6 +5767,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
> action=(ct_snat_in_czone;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
> flags.use_snat_zone == 1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
> action=(ct_snat_in_czone;)
> @@ -5754,10 +5778,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
>  ])
>
> @@ -5776,10 +5802,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
>    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src ==
> 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat_in_czone(172.168.0.10);)
>    table=??(lr_out_snat        ), priority=154  , match=(ip && ip4.src ==
> 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl) && reg9[[4]]
> == 1), action=(reg9[[4]] = 0; ct_snat(172.168.0.10);)
> @@ -5799,6 +5827,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.30 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> @@ -5806,10 +5835,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);)
>  ])
>
> @@ -5824,24 +5855,20 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src ==
> 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_next(snat);)
> -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src ==
> 10.0.0.10 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_next(snat);)
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> -  table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.dst ==
> 10.0.0.0/24 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src ==
> 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.168.0.10);)
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
> 10.0.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
> 10.0.0.10 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.168.0.30);)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
> 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")
> && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.20);)
>  ])
>
>  AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_post_snat   ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_snat   ), priority=153  , match=(ip && ip4.dst ==
> 10.0.0.0/24 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && ct.new),
> action=(ct_commit_to_zone(snat);)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
> 10.0.0.10 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")
> && ct.new), action=(ct_commit_to_zone(snat);)
>  ])
>
>  # Associate load balancer to lr0
> @@ -5866,6 +5893,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
> action=(ct_snat_in_czone;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
> flags.use_snat_zone == 1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
> action=(ct_snat_in_czone;)
> @@ -5876,6 +5904,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.100), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.200), action=(ct_dnat;)
> @@ -5884,6 +5913,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.0.200 &&
> is_chassis_resident("cr-lr0-public")),
> action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 &&
> is_chassis_resident("cr-lr0-public")),
> action=(ct_lb_mark(backends=10.0.0.4:8080);)
> @@ -5916,10 +5946,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
>    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src ==
> 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat_in_czone(172.168.0.10);)
>    table=??(lr_out_snat        ), priority=154  , match=(ip && ip4.src ==
> 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl) && reg9[[4]]
> == 1), action=(reg9[[4]] = 0; ct_snat(172.168.0.10);)
> @@ -5939,6 +5971,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.30 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> @@ -5946,6 +5979,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.100), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.200), action=(ct_dnat;)
> @@ -5954,6 +5988,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.0.200 &&
> is_chassis_resident("cr-lr0-public")),
> action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 &&
> is_chassis_resident("cr-lr0-public")),
> action=(ct_lb_mark(backends=10.0.0.4:8080);)
> @@ -5982,24 +6017,20 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src ==
> 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_next(snat);)
> -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src ==
> 10.0.0.10 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_next(snat);)
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> -  table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.dst ==
> 10.0.0.0/24 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src ==
> 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.168.0.10);)
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
> 10.0.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
> 10.0.0.10 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.168.0.30);)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
> 10.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")
> && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.20);)
>  ])
>
>  AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_post_snat   ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_snat   ), priority=153  , match=(ip && ip4.dst ==
> 10.0.0.0/24 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && ct.new),
> action=(ct_commit_to_zone(snat);)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
> 10.0.0.10 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")
> && ct.new), action=(ct_commit_to_zone(snat);)
>  ])
>
>  # Make the logical router as Gateway router
> @@ -6013,6 +6044,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
> 172.168.0.10), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
> 172.168.0.20), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
> 172.168.0.30), action=(ct_snat;)
> @@ -6020,6 +6052,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.100), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.200), action=(ct_dnat;)
> @@ -6028,6 +6061,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.0.200),
> action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(ct_lb_mark(backends=10.0.0.4:8080);)
> @@ -6053,11 +6087,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
>    table=??(lr_out_snat        ), priority=25   , match=(ip && ip4.src ==
> 10.0.0.0/24 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);)
>    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src ==
> 10.0.0.10 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.30);)
> @@ -6074,6 +6109,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
> 172.168.0.10), action=(ct_snat;)
> @@ -6083,6 +6119,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.100), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.200), action=(ct_dnat;)
> @@ -6091,6 +6128,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.0.200), action=(flags.force_snat_for_lb = 1;
> ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
> @@ -6116,11 +6154,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
> action=(ct_snat(172.168.0.10);)
>    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
> action=(ct_snat(10.0.0.1);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> @@ -6138,6 +6177,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
> 172.168.0.10), action=(ct_snat;)
> @@ -6147,6 +6187,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.100), action=(ct_dnat;)
> @@ -6156,6 +6197,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.0.200), action=(flags.force_snat_for_lb = 1;
> ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
> @@ -6182,11 +6224,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
> action=(ct_snat(172.168.0.10);)
>    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
> action=(ct_snat(10.0.0.1);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> @@ -6212,6 +6255,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip6.dst == def0::10), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
> @@ -6223,6 +6267,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.100), action=(ct_dnat;)
> @@ -6233,6 +6278,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.0.200), action=(flags.force_snat_for_lb = 1;
> ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
> @@ -6260,11 +6306,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
> action=(ct_snat(172.168.0.10);)
>    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
> action=(ct_snat(10.0.0.1);)
>    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip6 && outport == "lr0-public"),
> action=(ct_snat(def0::10);)
> @@ -6291,15 +6338,18 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
>  ])
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
> 172.168.0.210), action=(ct_dnat;)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.0.210 && tcp && tcp.dst == 60),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.50:6062,
> 10.0.0.60:6062; force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.0.210 && udp && udp.dst == 60),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.50:6062,
> 10.0.0.60:6062; force_snat);)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel
> && !ct.new && ct_mark.natted), action=(next;)
> @@ -6322,11 +6372,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk ||
> !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
>  ])
>
> @@ -6358,6 +6409,7 @@ check ovn-nbctl --wait=sb sync
>
>  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
> ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.10.10), action=(reg0 = 0; reject { outport <->
> inport; next(pipeline=egress,table=??);};)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel
> && !ct.new && ct_mark.natted), action=(next;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est
> && !ct.new), action=(ct_commit_nat;)
> @@ -6372,6 +6424,7 @@ check ovn-nbctl --wait=sb set load_balancer lb5
> options:skip_snat=true
>
>  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
> ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.10.10), action=(flags.skip_snat_for_lb = 1;
> reg0 = 0; reject { outport <-> inport; next(pipeline=egress,table=??);};)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel
> && !ct.new && ct_mark.natted), action=(next;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est
> && !ct.new), action=(ct_commit_nat;)
> @@ -6388,6 +6441,7 @@ check ovn-nbctl --wait=sb set logical_router lr0
> options:lb_force_snat_ip="route
>
>  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
> ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.10.10), action=(flags.force_snat_for_lb = 1;
> reg0 = 0; reject { outport <-> inport; next(pipeline=egress,table=??);};)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel
> && !ct.new && ct_mark.natted), action=(next;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est
> && !ct.new), action=(ct_commit_nat;)
> @@ -6405,6 +6459,7 @@ check ovn-nbctl --wait=sb lr-lb-add lr0 lb6
>
>  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
> ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.10.30), action=(drop;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel
> && !ct.new && ct_mark.natted), action=(next;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est
> && !ct.new), action=(ct_commit_nat;)
> @@ -6419,6 +6474,7 @@ check ovn-nbctl --wait=sb set load_balancer lb6
> options:skip_snat=true
>
>  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
> ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.10.30), action=(flags.skip_snat_for_lb = 1;
> drop;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel
> && !ct.new && ct_mark.natted), action=(next;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est
> && !ct.new), action=(ct_commit_nat;)
> @@ -6435,6 +6491,7 @@ check ovn-nbctl --wait=sb set logical_router lr0
> options:lb_force_snat_ip="route
>
>  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
> ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.168.10.30), action=(flags.force_snat_for_lb = 1;
> drop;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est && !ct.rel
> && !ct.new && ct_mark.natted), action=(next;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel && !ct.est
> && !ct.new), action=(ct_commit_nat;)
> @@ -7951,9 +8008,6 @@ AT_CHECK([grep lr_in_unsnat lrflows | grep ct_snat |
> ovn_strip_lflows], [0], [dn
>  ])
>
>  AT_CHECK([grep lr_out_snat lrflows | grep ct_snat | ovn_strip_lflows],
> [0], [dnl
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
> 20.0.0.10 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1")),
> action=(ct_snat;)
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
> 20.0.0.10 && inport == "DR-S2" && is_chassis_resident("cr-DR-S2")),
> action=(ct_snat;)
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
> 20.0.0.10 && inport == "DR-S3" && is_chassis_resident("cr-DR-S3")),
> action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
> 20.0.0.10 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.10);)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
> 20.0.0.10 && outport == "DR-S2" && is_chassis_resident("cr-DR-S2") &&
> (!ct.trk || !ct.rpl)), action=(ct_snat(10.0.0.10);)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
> 20.0.0.10 && outport == "DR-S3" && is_chassis_resident("cr-DR-S3") &&
> (!ct.trk || !ct.rpl)), action=(ct_snat(192.168.0.10);)
> @@ -7961,9 +8015,6 @@ AT_CHECK([grep lr_out_snat lrflows | grep ct_snat |
> ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep lr_out_post_snat lrflows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_post_snat   ), priority=0    , match=(1), action=(next;)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
> 20.0.0.10 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ct.new), action=(ct_commit_to_zone(snat);)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
> 20.0.0.10 && inport == "DR-S2" && is_chassis_resident("cr-DR-S2") &&
> ct.new), action=(ct_commit_to_zone(snat);)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
> 20.0.0.10 && inport == "DR-S3" && is_chassis_resident("cr-DR-S3") &&
> ct.new), action=(ct_commit_to_zone(snat);)
>  ])
>
>  check ovn-nbctl --wait=sb lr-nat-del DR snat 20.0.0.10
> @@ -9387,6 +9438,7 @@ AT_CHECK([grep "lr_in_lb_aff_check" R1flows |
> ovn_strip_lflows], [0], [dnl
>  ])
>  AT_CHECK([grep "lr_in_dnat " R1flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
> action=(ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);)
>    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 &&
> ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 &&
> ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 && reg8[[0..15]] ==
> 80), action=(reg0 = 172.16.0.10; ct_lb_mark(backends=20.0.0.2:80);)
> @@ -9411,6 +9463,7 @@ AT_CAPTURE_FILE([R1flows_skip_snat])
>
>  AT_CHECK([grep "lr_in_dnat " R1flows_skip_snat | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
> action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
> 20.0.0.2:80; skip_snat);)
>    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 &&
> ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 &&
> ct.new && ip4.dst == 172.16.0.10 && 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);)
> @@ -9432,6 +9485,7 @@ AT_CAPTURE_FILE([R1flows_force_snat])
>
>  AT_CHECK([grep "lr_in_dnat " R1flows_force_snat | ovn_strip_lflows], [0],
> [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
> 20.0.0.2:80; force_snat);)
>    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 &&
> ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 &&
> ct.new && ip4.dst == 172.16.0.10 && 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);)
> @@ -9452,6 +9506,7 @@ AT_CAPTURE_FILE([R1flows_force_skip_snat])
>
>  AT_CHECK([grep "lr_in_dnat " R1flows_force_skip_snat | ovn_strip_lflows],
> [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
> action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
> 20.0.0.2:80; skip_snat);)
>    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 &&
> ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 &&
> ct.new && ip4.dst == 172.16.0.10 && 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);)
> @@ -9476,6 +9531,7 @@ AT_CAPTURE_FILE([R1flows_2lbs])
>
>  AT_CHECK([grep "lr_in_dnat " R1flows_2lbs | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1), action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
> action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
> 20.0.0.2:80; skip_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new && !ct.rel
> && ip4 && ip4.dst == 172.16.0.20 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
> 20.0.0.2:80; force_snat);)
>    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1 &&
> ct.new && ip4.dst == 172.16.0.10 && 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);)
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index 861b1cb99..dc7b0ab2e 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -117,6 +117,7 @@ NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2
> 30.0.0.2 | FORMAT_PING], \
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.2) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
>  icmp,orig=(src=172.16.1.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
> +icmp,orig=(src=172.16.1.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
>  icmp,orig=(src=172.16.1.2,dst=30.0.0.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>  ])
>
> @@ -297,6 +298,7 @@ NS_CHECK_EXEC([alice1], [ping6 -q -c 3 -i 0.3 -w 2
> fd30::2 | FORMAT_PING], \
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd21::2) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
>  icmpv6,orig=(src=fd21::2,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>
> +icmpv6,orig=(src=fd21::2,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>
>  icmpv6,orig=(src=fd21::2,dst=fd30::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>  ])
>
> @@ -3753,6 +3755,7 @@ NS_CHECK_EXEC([foo2], [ping6 -q -c 3 -i 0.3 -w 2
> fd20::2 | FORMAT_PING], \
>  ovs-appctl dpctl/dump-conntrack | grep icmpv6
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd11::3) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
> +icmpv6,orig=(src=fd11::3,dst=fd20::2,id=<cleared>,type=128,code=0),reply=(src=fd20::2,dst=fd11::3,id=<cleared>,type=129,code=0),zone=<cleared>
>  ])
>
>  # We verify that SNAT indeed happened via 'dump-conntrack' command.
> @@ -3938,6 +3941,8 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2
> 192.168.2.2 | FORMAT_PING], \
>  # We verify that the connection is not tracked.
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(192.168.2.2) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
> +icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
> +icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>  ])
>
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> @@ -3950,6 +3955,8 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
> 192.168.2.2 | FORMAT_PING], \
>  # We verify that the connection is not tracked.
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(192.168.2.2) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
> +icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>
> +icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>  ])
>
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> @@ -3959,9 +3966,11 @@ NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2
> 192.168.1.3 | FORMAT_PING], \
>  3 packets transmitted, 3 received, 0% packet loss, time 0ms
>  ])
>
> -# We verify that the connection is not tracked.
> +# We verify that the connection is tracked.
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(192.168.2.2) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
> +icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
> +icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>  ])
>
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> @@ -3978,6 +3987,7 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep
> icmp | FORMAT_CT(172.16.1.4) |
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
>  icmp,orig=(src=172.16.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>
>  icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>
> +icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>  ])
>
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> @@ -3993,7 +4003,6 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
> 172.16.1.4 | FORMAT_PING], \
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(172.16.1.1) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
>  icmp,orig=(src=172.16.1.1,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>
> -icmp,orig=(src=172.16.1.1,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>
>  icmp,orig=(src=192.168.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>  ])
>
> @@ -4144,6 +4153,7 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2
> fd20::4 | FORMAT_PING], \
>  # Then DNAT of 'bar1' address happens (listed first below).
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::4) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
> +icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd11::2,id=<cleared>,type=129,code=0),zone=<cleared>
>
>  icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
>
>  icmpv6,orig=(src=fd20::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
>  ])
> @@ -4161,7 +4171,6 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
> fd20::4 | FORMAT_PING], \
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
>  icmpv6,orig=(src=fd11::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>
> -icmpv6,orig=(src=fd20::1,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>
>  icmpv6,orig=(src=fd20::1,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>  ])
>
> @@ -8682,10 +8691,10 @@ test_ping sw11 192.168.1.2
>  OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep -v "n_packets=0" |
> grep 'nat(src=172.16.1.21)'])
>  # Ensure conntrack entry is present
>  OVS_WAIT_FOR_OUTPUT([
> -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
> +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
> -icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
> -tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>
> +icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>
> +tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>  ])
>
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> @@ -8697,9 +8706,11 @@ test_ping sw11 192.168.1.2
>
>  # Ensure conntrack entry is present
>  OVS_WAIT_FOR_OUTPUT([
> -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
> +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
> +icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>
>  icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
> +tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>
>  tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>  ])
>
> @@ -8711,10 +8722,10 @@ test_ping sw11 172.16.1.2
>
>  # Ensure conntrack entry is present
>  OVS_WAIT_FOR_OUTPUT([
> -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
> +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
> -icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
> -tcp,orig=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>
> +icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>
> +tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>  ])
>
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> --
> 2.46.2
>
>
Forgot to CC Han.
Han Zhou Nov. 13, 2024, 9:37 a.m. UTC | #2
Thanks Ales.

On Tue, Oct 8, 2024 at 3:12 AM Ales Musil <amusil@redhat.com> wrote:
>
> Commit all traffic that is not already commit by either NAT or LB. This
> ensures that the traffic is tracked, and we don't erroneously commit
> reply traffic, or reply traffic is not marked as invalid.
>
> To achieve the commit we need to perform lookup on every packet
> that goes through LR pipeline whenever there is stateful NAT.
>
> The SNAT lookup requires additional flag as the unSNAT is happening
> in ingress pipeline and at that point we need to know if the packet
> is reply or not. This is not required for DNAT, because unDNAT stage
> happens in egress.

Could you help explain in a little more detail how the ct.rpl relates to
this new without_unsnat flag? I still can't figure out why this flag is
needed.

In addition, it seems that with this flag, you commit to snat zone only if
the packet's without_unsnat == 1, but even if the packet did perform unSNAT
we would still want to commit it to SNAT zone, because of the HW offload
issue reported here:
https://mail.openvswitch.org/pipermail/ovs-dev/2024-August/417025.html

BTW, I have been trying my best but I feel it is quite difficult to reason
about the correctness of the DNAT/SNAT pipelines, which have become so
complex. I hope our test case coverage is sufficient for all kinds of
corner cases :)

I am still working on performance test for this patch. Sorry it took so
long but I still need some more time.

Regards,
Han

>
> Signed-off-by: Ales Musil <amusil@redhat.com>
> ---
> There is one failing system test with userspace datapath, that's due
> to the recirculation limit that is being hit due to additional
> lookups.
> ---
>  include/ovn/logical-fields.h |   4 ++
>  lib/logical-fields.c         |   4 ++
>  northd/northd.c              |  76 ++++++++++++----------
>  tests/ovn-northd.at          | 118 ++++++++++++++++++++++++++---------
>  tests/system-ovn.at          |  31 ++++++---
>  5 files changed, 158 insertions(+), 75 deletions(-)
>
> diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
> index d6c4a9b6b..cc1f50ff2 100644
> --- a/include/ovn/logical-fields.h
> +++ b/include/ovn/logical-fields.h
> @@ -82,6 +82,7 @@ enum mff_log_flags_bits {
>      MLF_LOCALNET_BIT = 15,
>      MLF_RX_FROM_TUNNEL_BIT = 16,
>      MLF_ICMP_SNAT_BIT = 17,
> +    MLF_WITHOUT_UNSNAT_BIT = 18,
>  };
>
>  /* MFF_LOG_FLAGS_REG flag assignments */
> @@ -137,6 +138,9 @@ enum mff_log_flags {
>      MLF_RX_FROM_TUNNEL = (1 << MLF_RX_FROM_TUNNEL_BIT),
>
>      MLF_ICMP_SNAT = (1 << MLF_ICMP_SNAT_BIT),
> +
> +    /* Indicate that the packet didn't go through unSNAT. */
> +    MLF_WITHOUT_UNSNAT = (1 << MLF_WITHOUT_UNSNAT_BIT),
>  };
>
>  /* OVN logical fields
> diff --git a/lib/logical-fields.c b/lib/logical-fields.c
> index 5a8b53f2b..c63e19897 100644
> --- a/lib/logical-fields.c
> +++ b/lib/logical-fields.c
> @@ -139,6 +139,10 @@ ovn_init_symtab(struct shash *symtab)
>                               flags_str);
>      snprintf(flags_str, sizeof flags_str, "flags[%d]",
MLF_RX_FROM_TUNNEL_BIT);
>      expr_symtab_add_subfield(symtab, "flags.tunnel_rx", NULL, flags_str);
> +    snprintf(flags_str, sizeof flags_str, "flags[%d]",
> +             MLF_WITHOUT_UNSNAT_BIT);
> +    expr_symtab_add_subfield(symtab, "flags.without_unsnat", NULL,
> +                             flags_str);
>
>      /* Connection tracking state. */
>      expr_symtab_add_field_scoped(symtab, "ct_mark", MFF_CT_MARK, NULL,
false,
> diff --git a/northd/northd.c b/northd/northd.c
> index 0364dd766..a42057e45 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -15987,8 +15987,7 @@ build_lrouter_out_snat_flow(struct lflow_table
*lflows,
>                              struct ds *actions, bool distributed_nat,
>                              struct eth_addr mac, int cidr_bits, bool
is_v6,
>                              struct ovn_port *l3dgw_port,
> -                            struct lflow_ref *lflow_ref,
> -                            const struct chassis_features *features)
> +                            struct lflow_ref *lflow_ref)
>  {
>      if (!(nat_entry->type == SNAT || nat_entry->type == DNAT_AND_SNAT)) {
>          return;
> @@ -16019,34 +16018,6 @@ build_lrouter_out_snat_flow(struct lflow_table
*lflows,
>                              priority, ds_cstr(match),
>                              ds_cstr(actions), &nat->header_,
>                              lflow_ref);
> -
> -    /* For the SNAT networks, we need to make sure that connections are
> -     * properly tracked so we can decide whether to perform SNAT on
traffic
> -     * exiting the network. */
> -    if (features->ct_commit_to_zone && features->ct_next_zone &&
> -        nat_entry->type == SNAT && !od->is_gw_router) {
> -        /* For traffic that comes from SNAT network, initiate CT state
before
> -         * entering S_ROUTER_OUT_SNAT to allow matching on various CT
states.
> -         */
> -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 70,
> -                      ds_cstr(match), "ct_next(snat);",
> -                      lflow_ref);
> -
> -        build_lrouter_out_snat_match(lflows, od, nat, match,
> -                                     distributed_nat, cidr_bits, is_v6,
> -                                     l3dgw_port, lflow_ref, true);
> -
> -        /* New traffic that goes into SNAT network is committed to CT to
avoid
> -         * SNAT-ing replies.*/
> -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, priority,
> -                      ds_cstr(match), "ct_snat;",
> -                      lflow_ref);
> -
> -        ds_put_cstr(match, " && ct.new");
> -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_SNAT, priority,
> -                      ds_cstr(match), "ct_commit_to_zone(snat);",
> -                      lflow_ref);
> -    }
>  }
>
>  static void
> @@ -16439,9 +16410,6 @@ build_lrouter_nat_defrag_and_lb(
>          ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 50,
>                        "ip", "flags.loopback = 1; ct_dnat;",
>                        lflow_ref);
> -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 50,
> -                      "ip && ct.new", "ct_commit { } ; next; ",
> -                      lflow_ref);
>      }
>
>      /* NAT rules are only valid on Gateway routers and routers with
> @@ -16459,6 +16427,9 @@ build_lrouter_nat_defrag_and_lb(
>          !lport_addresses_is_empty(&lrnat_rec->dnat_force_snat_addrs);
>      bool lb_force_snat_ip =
>          !lport_addresses_is_empty(&lrnat_rec->lb_force_snat_addrs);
> +    bool stateful_dnat = lr_stateful_rec->has_lb_vip;
> +    bool stateful_snat = (dnat_force_snat_ip || lb_force_snat_ip ||
> +                          lrnat_rec->lb_force_snat_router_ip);
>
>      for (size_t i = 0; i < lrnat_rec->n_nat_entries; i++) {
>          struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i];
> @@ -16477,6 +16448,21 @@ build_lrouter_nat_defrag_and_lb(
>              continue;
>          }
>
> +        if (!stateless) {
> +            switch (nat_entry->type) {
> +            case DNAT:
> +                stateful_dnat = true;
> +                break;
> +            case SNAT:
> +                stateful_snat = true;
> +                break;
> +            case DNAT_AND_SNAT:
> +                stateful_snat = true;
> +                stateful_dnat = true;
> +                break;
> +            }
> +        }
> +
>          /* S_ROUTER_IN_UNSNAT
>           * Ingress UNSNAT table: It is for already established
connections'
>           * reverse traffic. i.e., SNAT has already been done in egress
> @@ -16599,7 +16585,7 @@ build_lrouter_nat_defrag_and_lb(
>          } else {
>              build_lrouter_out_snat_flow(lflows, od, nat_entry, match,
actions,
>                                          distributed_nat, mac, cidr_bits,
is_v6,
> -                                        l3dgw_port, lflow_ref, features);
> +                                        l3dgw_port, lflow_ref);
>          }
>
>          /* S_ROUTER_IN_ADMISSION - S_ROUTER_IN_IP_INPUT */
> @@ -16689,6 +16675,28 @@ build_lrouter_nat_defrag_and_lb(
>          }
>      }
>
> +
> +    bool can_commit = features->ct_commit_to_zone &&
features->ct_next_zone;
> +    if (can_commit && stateful_dnat) {
> +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 10,
> +                      "ip && (!ct.trk || !ct.rpl)",
> +                      "ct_next(dnat);", lflow_ref);
> +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 10,
> +                      "ip && ct.new", "ct_commit_to_zone(dnat);",
lflow_ref);
> +    }
> +
> +    if (can_commit && stateful_snat) {
> +        ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 10,
> +                      "ip", "flags.without_unsnat = 1; next;",
lflow_ref);
> +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 10,
> +                      "ip && (!ct.trk || !ct.rpl) && "
> +                      "flags.without_unsnat == 1", "ct_next(snat);",
> +                      lflow_ref);
> +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 10,
> +                      "ip && ct.new && flags.without_unsnat == 1",
> +                      "ct_commit_to_zone(snat);", lflow_ref);
> +    }
> +
>      if (use_common_zone && od->nbr->n_nat) {
>          ds_clear(match);
>          ds_put_cstr(match, "ip && ct_mark.natted == 1");
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index d6a8c4640..96e28a54a 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -1181,18 +1181,18 @@ AT_CAPTURE_FILE([crflows])
>
>  AT_CHECK([grep -e "lr_out_snat" drflows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
ip4.src == $allowed_range), action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.16.1.1);)
>  ])
>
>  AT_CHECK([grep -e "lr_out_post_snat" drflows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_snat   ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
ip4.src == $allowed_range && ct.new), action=(ct_commit_to_zone(snat);)
>  ])
>
>  AT_CHECK([grep -e "lr_out_snat" crflows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src ==
50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.16.1.1);)
>  ])
> @@ -1220,19 +1220,19 @@ AT_CAPTURE_FILE([crflows2])
>
>  AT_CHECK([grep -e "lr_out_snat" drflows2 | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1")),
action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
(!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
>    table=??(lr_out_snat        ), priority=163  , match=(ip && ip4.src ==
50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
ip4.dst == $disallowed_range), action=(next;)
>  ])
>
>  AT_CHECK([grep -e "lr_out_post_snat" drflows2 | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_snat   ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
ct.new), action=(ct_commit_to_zone(snat);)
>  ])
>
>  AT_CHECK([grep -e "lr_out_snat" crflows2 | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src ==
50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
>    table=??(lr_out_snat        ), priority=35   , match=(ip && ip4.src ==
50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
> @@ -1259,6 +1259,7 @@ AT_CAPTURE_FILE([crflows2])
>
>  AT_CHECK([grep -e "lr_out_snat" drflows3 | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.16.1.2);)
>  ])
> @@ -1269,6 +1270,7 @@ AT_CHECK([grep -e "lr_out_post_snat" drflows3 |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep -e "lr_out_snat" crflows3 | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src ==
50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.16.1.2);)
>  ])
> @@ -1294,6 +1296,7 @@ AT_CAPTURE_FILE([crflows2])
>
>  AT_CHECK([grep -e "lr_out_snat" drflows4 | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
(!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
>    table=??(lr_out_snat        ), priority=163  , match=(ip && ip4.src ==
50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
ip4.dst == $disallowed_range), action=(next;)
> @@ -1301,6 +1304,7 @@ AT_CHECK([grep -e "lr_out_snat" drflows4 |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep -e "lr_out_snat" crflows4 | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src ==
50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
>    table=??(lr_out_snat        ), priority=35   , match=(ip && ip4.src ==
50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
> @@ -1663,6 +1667,7 @@ AT_CAPTURE_FILE([sbflows])
>  # dnat_and_snat or snat entry.
>  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
192.168.2.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
192.168.2.4), action=(ct_snat;)
>  ])
> @@ -1693,6 +1698,7 @@ AT_CAPTURE_FILE([sbflows])
>  # dnat_and_snat or snat entry.
>  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
192.168.2.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
192.168.2.4), action=(ct_snat;)
>  ])
> @@ -1801,6 +1807,7 @@ ovn-nbctl --wait=sb sync
>
>  AT_CHECK([ovn-sbctl lflow-list lr0 | grep lr_in_unsnat |
ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 && ip4.dst
== 192.168.2.3), action=(ct_snat;)
>  ])
>
> @@ -4277,12 +4284,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.100), action=(ct_dnat;)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(ct_lb_mark(backends=10.0.0.4:8080);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
action=(ct_lb_mark(backends=10.0.0.40:8080);)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
> @@ -4302,18 +4311,21 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 && ip4.dst
== 20.0.0.4), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(ip6 && ip6.dst
== aef0::4), action=(ct_snat;)
>  ])
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.100), action=(ct_dnat;)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
force_snat);)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
> @@ -4326,6 +4338,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=100  ,
match=(flags.force_snat_for_lb == 1 && ip4), action=(ct_snat(20.0.0.4);)
>    table=??(lr_out_snat        ), priority=100  ,
match=(flags.force_snat_for_lb == 1 && ip6), action=(ct_snat(aef0::4);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
> @@ -4339,7 +4352,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  check ovn-nbctl --wait=sb set logical_router lr0
options:lb_force_snat_ip="router_ip"
> @@ -4352,6 +4365,7 @@ AT_CHECK([grep "lr_in_ip_input" lr0flows | grep
"priority=60" | sort], [0], [dnl
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
> @@ -4359,12 +4373,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.100), action=(ct_dnat;)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
force_snat);)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
> @@ -4377,6 +4393,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
action=(ct_snat(172.168.0.100);)
>    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
action=(ct_snat(10.0.0.1);)
>    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
action=(ct_snat(20.0.0.1);)
> @@ -4391,7 +4408,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  check ovn-nbctl --wait=sb remove logical_router lr0 options chassis
> @@ -4416,6 +4433,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
> @@ -4424,12 +4442,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.100), action=(ct_dnat;)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
force_snat);)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
> @@ -4442,6 +4462,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
action=(ct_snat(172.168.0.100);)
>    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
action=(ct_snat(10.0.0.1);)
>    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
action=(ct_snat(20.0.0.1);)
> @@ -4457,7 +4478,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  check ovn-nbctl --wait=sb lb-add lb2 10.0.0.20:80 10.0.0.40:8080
> @@ -4468,6 +4489,7 @@ ovn-sbctl dump-flows lr0 > lr0flows
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
> @@ -4476,6 +4498,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.100), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.20), action=(ct_dnat;)
>  ])
> @@ -4498,7 +4521,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CLEANUP
> @@ -5744,6 +5767,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
action=(ct_snat_in_czone;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
flags.use_snat_zone == 1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
action=(ct_snat_in_czone;)
> @@ -5754,10 +5778,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
>  ])
>
> @@ -5776,10 +5802,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src ==
10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat_in_czone(172.168.0.10);)
>    table=??(lr_out_snat        ), priority=154  , match=(ip && ip4.src ==
10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl) && reg9[[4]]
== 1), action=(reg9[[4]] = 0; ct_snat(172.168.0.10);)
> @@ -5799,6 +5827,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.30 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> @@ -5806,10 +5835,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);)
>  ])
>
> @@ -5824,24 +5855,20 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src ==
10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_next(snat);)
> -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src ==
10.0.0.10 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_next(snat);)
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
> -  table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.dst ==
10.0.0.0/24 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src ==
10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.168.0.10);)
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
10.0.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
10.0.0.10 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.168.0.30);)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
10.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")
&& (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.20);)
>  ])
>
>  AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_post_snat   ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_snat   ), priority=153  , match=(ip && ip4.dst ==
10.0.0.0/24 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && ct.new),
action=(ct_commit_to_zone(snat);)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
10.0.0.10 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")
&& ct.new), action=(ct_commit_to_zone(snat);)
>  ])
>
>  # Associate load balancer to lr0
> @@ -5866,6 +5893,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
action=(ct_snat_in_czone;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
flags.use_snat_zone == 1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
action=(ct_snat_in_czone;)
> @@ -5876,6 +5904,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.100), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.200), action=(ct_dnat;)
> @@ -5884,6 +5913,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.200 &&
is_chassis_resident("cr-lr0-public")),
action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 &&
is_chassis_resident("cr-lr0-public")),
action=(ct_lb_mark(backends=10.0.0.4:8080);)
> @@ -5916,10 +5946,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src ==
10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat_in_czone(172.168.0.10);)
>    table=??(lr_out_snat        ), priority=154  , match=(ip && ip4.src ==
10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl) && reg9[[4]]
== 1), action=(reg9[[4]] = 0; ct_snat(172.168.0.10);)
> @@ -5939,6 +5971,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.30 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> @@ -5946,6 +5979,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.100), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.200), action=(ct_dnat;)
> @@ -5954,6 +5988,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.200 &&
is_chassis_resident("cr-lr0-public")),
action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 &&
is_chassis_resident("cr-lr0-public")),
action=(ct_lb_mark(backends=10.0.0.4:8080);)
> @@ -5982,24 +6017,20 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src ==
10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_next(snat);)
> -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src ==
10.0.0.10 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_next(snat);)
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
> -  table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.dst ==
10.0.0.0/24 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src ==
10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.168.0.10);)
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
10.0.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
10.0.0.10 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.168.0.30);)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
10.0.0.3 && outport == "lr0-public" && is_chassis_resident("cr-lr0-public")
&& (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.20);)
>  ])
>
>  AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_post_snat   ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_snat   ), priority=153  , match=(ip && ip4.dst ==
10.0.0.0/24 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && ct.new),
action=(ct_commit_to_zone(snat);)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
10.0.0.10 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")
&& ct.new), action=(ct_commit_to_zone(snat);)
>  ])
>
>  # Make the logical router as Gateway router
> @@ -6013,6 +6044,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
172.168.0.10), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
172.168.0.20), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
172.168.0.30), action=(ct_snat;)
> @@ -6020,6 +6052,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.100), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.200), action=(ct_dnat;)
> @@ -6028,6 +6061,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.200),
action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(ct_lb_mark(backends=10.0.0.4:8080);)
> @@ -6053,11 +6087,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>    table=??(lr_out_snat        ), priority=25   , match=(ip && ip4.src ==
10.0.0.0/24 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);)
>    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src ==
10.0.0.10 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.30);)
> @@ -6074,6 +6109,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
172.168.0.10), action=(ct_snat;)
> @@ -6083,6 +6119,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.100), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.200), action=(ct_dnat;)
> @@ -6091,6 +6128,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.200),
action=(flags.force_snat_for_lb = 1;
ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
force_snat);)
> @@ -6116,11 +6154,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
action=(ct_snat(172.168.0.10);)
>    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
action=(ct_snat(10.0.0.1);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
> @@ -6138,6 +6177,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst ==
172.168.0.10), action=(ct_snat;)
> @@ -6147,6 +6187,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.100), action=(ct_dnat;)
> @@ -6156,6 +6197,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.200),
action=(flags.force_snat_for_lb = 1;
ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
force_snat);)
> @@ -6182,11 +6224,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
action=(ct_snat(172.168.0.10);)
>    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
action=(ct_snat(10.0.0.1);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
> @@ -6212,6 +6255,7 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip6.dst == def0::10), action=(ct_snat;)
>    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
> @@ -6223,6 +6267,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
10.0.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.10), action=(ct_dnat;)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.100), action=(ct_dnat;)
> @@ -6233,6 +6278,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst ==
172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.200),
action=(flags.force_snat_for_lb = 1;
ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
force_snat);)
> @@ -6260,11 +6306,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
action=(ct_snat(172.168.0.10);)
>    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
action=(ct_snat(10.0.0.1);)
>    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip6 && outport == "lr0-public"),
action=(ct_snat(def0::10);)
> @@ -6291,15 +6338,18 @@ AT_CAPTURE_FILE([lr0flows])
>
>  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>  ])
>
>  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl)), action=(ct_next(dnat);)
>    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst ==
172.168.0.210), action=(ct_dnat;)
>  ])
>
>  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.210 && tcp && tcp.dst == 60),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.50:6062,
10.0.0.60:6062; force_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.210 && udp && udp.dst == 60),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.50:6062,
10.0.0.60:6062; force_snat);)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
> @@ -6322,11 +6372,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
action=(ct_commit { } ; next; )
> +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
|| !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>  ])
>
>  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>  ])
>
> @@ -6358,6 +6409,7 @@ check ovn-nbctl --wait=sb sync
>
>  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.10.10), action=(reg0 = 0; reject {
outport <-> inport; next(pipeline=egress,table=??);};)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
!ct.est && !ct.new), action=(ct_commit_nat;)
> @@ -6372,6 +6424,7 @@ check ovn-nbctl --wait=sb set load_balancer lb5
options:skip_snat=true
>
>  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.10.10), action=(flags.skip_snat_for_lb
= 1; reg0 = 0; reject { outport <-> inport;
next(pipeline=egress,table=??);};)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
!ct.est && !ct.new), action=(ct_commit_nat;)
> @@ -6388,6 +6441,7 @@ check ovn-nbctl --wait=sb set logical_router lr0
options:lb_force_snat_ip="route
>
>  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.10.10),
action=(flags.force_snat_for_lb = 1; reg0 = 0; reject { outport <-> inport;
next(pipeline=egress,table=??);};)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
!ct.est && !ct.new), action=(ct_commit_nat;)
> @@ -6405,6 +6459,7 @@ check ovn-nbctl --wait=sb lr-lb-add lr0 lb6
>
>  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.10.30), action=(drop;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
!ct.est && !ct.new), action=(ct_commit_nat;)
> @@ -6419,6 +6474,7 @@ check ovn-nbctl --wait=sb set load_balancer lb6
options:skip_snat=true
>
>  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.10.30), action=(flags.skip_snat_for_lb
= 1; drop;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
!ct.est && !ct.new), action=(ct_commit_nat;)
> @@ -6435,6 +6491,7 @@ check ovn-nbctl --wait=sb set logical_router lr0
options:lb_force_snat_ip="route
>
>  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.10.30),
action=(flags.force_snat_for_lb = 1; drop;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
!ct.est && !ct.new), action=(ct_commit_nat;)
> @@ -7951,9 +8008,6 @@ AT_CHECK([grep lr_in_unsnat lrflows | grep ct_snat
| ovn_strip_lflows], [0], [dn
>  ])
>
>  AT_CHECK([grep lr_out_snat lrflows | grep ct_snat | ovn_strip_lflows],
[0], [dnl
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
20.0.0.10 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1")),
action=(ct_snat;)
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
20.0.0.10 && inport == "DR-S2" && is_chassis_resident("cr-DR-S2")),
action=(ct_snat;)
> -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst ==
20.0.0.10 && inport == "DR-S3" && is_chassis_resident("cr-DR-S3")),
action=(ct_snat;)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
20.0.0.10 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
(!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.10);)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
20.0.0.10 && outport == "DR-S2" && is_chassis_resident("cr-DR-S2") &&
(!ct.trk || !ct.rpl)), action=(ct_snat(10.0.0.10);)
>    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src ==
20.0.0.10 && outport == "DR-S3" && is_chassis_resident("cr-DR-S3") &&
(!ct.trk || !ct.rpl)), action=(ct_snat(192.168.0.10);)
> @@ -7961,9 +8015,6 @@ AT_CHECK([grep lr_out_snat lrflows | grep ct_snat |
ovn_strip_lflows], [0], [dnl
>
>  AT_CHECK([grep lr_out_post_snat lrflows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_out_post_snat   ), priority=0    , match=(1),
action=(next;)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
20.0.0.10 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
ct.new), action=(ct_commit_to_zone(snat);)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
20.0.0.10 && inport == "DR-S2" && is_chassis_resident("cr-DR-S2") &&
ct.new), action=(ct_commit_to_zone(snat);)
> -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst ==
20.0.0.10 && inport == "DR-S3" && is_chassis_resident("cr-DR-S3") &&
ct.new), action=(ct_commit_to_zone(snat);)
>  ])
>
>  check ovn-nbctl --wait=sb lr-nat-del DR snat 20.0.0.10
> @@ -9387,6 +9438,7 @@ AT_CHECK([grep "lr_in_lb_aff_check" R1flows |
ovn_strip_lflows], [0], [dnl
>  ])
>  AT_CHECK([grep "lr_in_dnat " R1flows | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
action=(ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);)
>    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
&& ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
&& ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 && reg8[[0..15]] ==
80), action=(reg0 = 172.16.0.10; ct_lb_mark(backends=20.0.0.2:80);)
> @@ -9411,6 +9463,7 @@ AT_CAPTURE_FILE([R1flows_skip_snat])
>
>  AT_CHECK([grep "lr_in_dnat " R1flows_skip_snat | ovn_strip_lflows], [0],
[dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
20.0.0.2:80; skip_snat);)
>    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
&& ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
&& ct.new && ip4.dst == 172.16.0.10 && 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);)
> @@ -9432,6 +9485,7 @@ AT_CAPTURE_FILE([R1flows_force_snat])
>
>  AT_CHECK([grep "lr_in_dnat " R1flows_force_snat | ovn_strip_lflows],
[0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
20.0.0.2:80; force_snat);)
>    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
&& ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
&& ct.new && ip4.dst == 172.16.0.10 && 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);)
> @@ -9452,6 +9506,7 @@ AT_CAPTURE_FILE([R1flows_force_skip_snat])
>
>  AT_CHECK([grep "lr_in_dnat " R1flows_force_skip_snat |
ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
20.0.0.2:80; skip_snat);)
>    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
&& ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
&& ct.new && ip4.dst == 172.16.0.10 && 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);)
> @@ -9476,6 +9531,7 @@ AT_CAPTURE_FILE([R1flows_2lbs])
>
>  AT_CHECK([grep "lr_in_dnat " R1flows_2lbs | ovn_strip_lflows], [0], [dnl
>    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
> +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
action=(ct_commit_to_zone(dnat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
20.0.0.2:80; skip_snat);)
>    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.16.0.20 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
20.0.0.2:80; force_snat);)
>    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
&& ct.new && ip4.dst == 172.16.0.10 && 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);)
> diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> index 861b1cb99..dc7b0ab2e 100644
> --- a/tests/system-ovn.at
> +++ b/tests/system-ovn.at
> @@ -117,6 +117,7 @@ NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2
30.0.0.2 | FORMAT_PING], \
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.2) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
 icmp,orig=(src=172.16.1.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
+icmp,orig=(src=172.16.1.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
 icmp,orig=(src=172.16.1.2,dst=30.0.0.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>  ])
>
> @@ -297,6 +298,7 @@ NS_CHECK_EXEC([alice1], [ping6 -q -c 3 -i 0.3 -w 2
fd30::2 | FORMAT_PING], \
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd21::2) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
 icmpv6,orig=(src=fd21::2,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>
+icmpv6,orig=(src=fd21::2,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>
 icmpv6,orig=(src=fd21::2,dst=fd30::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>  ])
>
> @@ -3753,6 +3755,7 @@ NS_CHECK_EXEC([foo2], [ping6 -q -c 3 -i 0.3 -w 2
fd20::2 | FORMAT_PING], \
>  ovs-appctl dpctl/dump-conntrack | grep icmpv6
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd11::3) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
+icmpv6,orig=(src=fd11::3,dst=fd20::2,id=<cleared>,type=128,code=0),reply=(src=fd20::2,dst=fd11::3,id=<cleared>,type=129,code=0),zone=<cleared>
>  ])
>
>  # We verify that SNAT indeed happened via 'dump-conntrack' command.
> @@ -3938,6 +3941,8 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2
192.168.2.2 | FORMAT_PING], \
>  # We verify that the connection is not tracked.
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
FORMAT_CT(192.168.2.2) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
+icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
+icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>  ])
>
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> @@ -3950,6 +3955,8 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
192.168.2.2 | FORMAT_PING], \
>  # We verify that the connection is not tracked.
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
FORMAT_CT(192.168.2.2) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
+icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>
+icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>  ])
>
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> @@ -3959,9 +3966,11 @@ NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2
192.168.1.3 | FORMAT_PING], \
>  3 packets transmitted, 3 received, 0% packet loss, time 0ms
>  ])
>
> -# We verify that the connection is not tracked.
> +# We verify that the connection is tracked.
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
FORMAT_CT(192.168.2.2) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
+icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
+icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>  ])
>
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> @@ -3978,6 +3987,7 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep
icmp | FORMAT_CT(172.16.1.4) |
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
 icmp,orig=(src=172.16.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>
 icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>
+icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>  ])
>
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> @@ -3993,7 +4003,6 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
172.16.1.4 | FORMAT_PING], \
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
FORMAT_CT(172.16.1.1) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
 icmp,orig=(src=172.16.1.1,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>
-icmp,orig=(src=172.16.1.1,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>
 icmp,orig=(src=192.168.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>  ])
>
> @@ -4144,6 +4153,7 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2
fd20::4 | FORMAT_PING], \
>  # Then DNAT of 'bar1' address happens (listed first below).
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::4) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
+icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd11::2,id=<cleared>,type=129,code=0),zone=<cleared>
>
 icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
>
 icmpv6,orig=(src=fd20::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
>  ])
> @@ -4161,7 +4171,6 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
fd20::4 | FORMAT_PING], \
>  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \
>  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
 icmpv6,orig=(src=fd11::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>
-icmpv6,orig=(src=fd20::1,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>
 icmpv6,orig=(src=fd20::1,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>  ])
>
> @@ -8682,10 +8691,10 @@ test_ping sw11 192.168.1.2
>  OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep -v "n_packets=0" |
grep 'nat(src=172.16.1.21)'])
>  # Ensure conntrack entry is present
>  OVS_WAIT_FOR_OUTPUT([
> -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
> +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
-icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
-tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>
+icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>
+tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>  ])
>
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> @@ -8697,9 +8706,11 @@ test_ping sw11 192.168.1.2
>
>  # Ensure conntrack entry is present
>  OVS_WAIT_FOR_OUTPUT([
> -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
> +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
+icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>
 icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
+tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>
 tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>  ])
>
> @@ -8711,10 +8722,10 @@ test_ping sw11 172.16.1.2
>
>  # Ensure conntrack entry is present
>  OVS_WAIT_FOR_OUTPUT([
> -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
> +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>
-icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>
-tcp,orig=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>
+icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>
+tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>  ])
>
>  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> --
> 2.46.2
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Ales Musil Nov. 13, 2024, 9:59 a.m. UTC | #3
On Wed, Nov 13, 2024 at 10:37 AM Han Zhou <hzhou@ovn.org> wrote:

> Thanks Ales.
>
>
Hi Han,


>
> On Tue, Oct 8, 2024 at 3:12 AM Ales Musil <amusil@redhat.com> wrote:
> >
> > Commit all traffic that is not already commit by either NAT or LB. This
> > ensures that the traffic is tracked, and we don't erroneously commit
> > reply traffic, or reply traffic is not marked as invalid.
> >
> > To achieve the commit we need to perform lookup on every packet
> > that goes through LR pipeline whenever there is stateful NAT.
> >
> > The SNAT lookup requires additional flag as the unSNAT is happening
> > in ingress pipeline and at that point we need to know if the packet
> > is reply or not. This is not required for DNAT, because unDNAT stage
> > happens in egress.
>
> Could you help explain in a little more detail how the ct.rpl relates to
> this new without_unsnat flag? I still can't figure out why this flag is
> needed.
>

Without it we would commit reply traffic for SNAT which is wrong. That
caused all sorts of issues.


>
> In addition, it seems that with this flag, you commit to snat zone only if
> the packet's without_unsnat == 1, but even if the packet did perform unSNAT
> we would still want to commit it to SNAT zone, because of the HW offload
> issue reported here:
> https://mail.openvswitch.org/pipermail/ovs-dev/2024-August/417025.html
>

I'm probably missing something, why would we want to commit traffic that
did unSNAT? That traffic is already committed in the SNAT zone. The issue
described in the thread should be gone, AFAIU the problem was that the
traffic wasn't committed in the original direction, then unSNAT would mark
it as new. That shouldn't happen with this series.


>
> BTW, I have been trying my best but I feel it is quite difficult to reason
> about the correctness of the DNAT/SNAT pipelines, which have become so
> complex. I hope our test case coverage is sufficient for all kinds of
> corner cases :)
>

Yeah I understand and we probably have decent coverage because some of my
thoughts were quickly disputed by failing and misbehaving tests :)

>
> I am still working on performance test for this patch. Sorry it took so
> long but I still need some more time.
>
>
This is one of the things that we shouldn't rush, thank you for looking
into the performance testing!


>
> Regards,
> Han
>
>
Regards,
Ales


> >
> > Signed-off-by: Ales Musil <amusil@redhat.com>
> > ---
> > There is one failing system test with userspace datapath, that's due
> > to the recirculation limit that is being hit due to additional
> > lookups.
> > ---
> >  include/ovn/logical-fields.h |   4 ++
> >  lib/logical-fields.c         |   4 ++
> >  northd/northd.c              |  76 ++++++++++++----------
> >  tests/ovn-northd.at          | 118 ++++++++++++++++++++++++++---------
> >  tests/system-ovn.at          |  31 ++++++---
> >  5 files changed, 158 insertions(+), 75 deletions(-)
> >
> > diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
> > index d6c4a9b6b..cc1f50ff2 100644
> > --- a/include/ovn/logical-fields.h
> > +++ b/include/ovn/logical-fields.h
> > @@ -82,6 +82,7 @@ enum mff_log_flags_bits {
> >      MLF_LOCALNET_BIT = 15,
> >      MLF_RX_FROM_TUNNEL_BIT = 16,
> >      MLF_ICMP_SNAT_BIT = 17,
> > +    MLF_WITHOUT_UNSNAT_BIT = 18,
> >  };
> >
> >  /* MFF_LOG_FLAGS_REG flag assignments */
> > @@ -137,6 +138,9 @@ enum mff_log_flags {
> >      MLF_RX_FROM_TUNNEL = (1 << MLF_RX_FROM_TUNNEL_BIT),
> >
> >      MLF_ICMP_SNAT = (1 << MLF_ICMP_SNAT_BIT),
> > +
> > +    /* Indicate that the packet didn't go through unSNAT. */
> > +    MLF_WITHOUT_UNSNAT = (1 << MLF_WITHOUT_UNSNAT_BIT),
> >  };
> >
> >  /* OVN logical fields
> > diff --git a/lib/logical-fields.c b/lib/logical-fields.c
> > index 5a8b53f2b..c63e19897 100644
> > --- a/lib/logical-fields.c
> > +++ b/lib/logical-fields.c
> > @@ -139,6 +139,10 @@ ovn_init_symtab(struct shash *symtab)
> >                               flags_str);
> >      snprintf(flags_str, sizeof flags_str, "flags[%d]",
> MLF_RX_FROM_TUNNEL_BIT);
> >      expr_symtab_add_subfield(symtab, "flags.tunnel_rx", NULL,
> flags_str);
> > +    snprintf(flags_str, sizeof flags_str, "flags[%d]",
> > +             MLF_WITHOUT_UNSNAT_BIT);
> > +    expr_symtab_add_subfield(symtab, "flags.without_unsnat", NULL,
> > +                             flags_str);
> >
> >      /* Connection tracking state. */
> >      expr_symtab_add_field_scoped(symtab, "ct_mark", MFF_CT_MARK, NULL,
> false,
> > diff --git a/northd/northd.c b/northd/northd.c
> > index 0364dd766..a42057e45 100644
> > --- a/northd/northd.c
> > +++ b/northd/northd.c
> > @@ -15987,8 +15987,7 @@ build_lrouter_out_snat_flow(struct lflow_table
> *lflows,
> >                              struct ds *actions, bool distributed_nat,
> >                              struct eth_addr mac, int cidr_bits, bool
> is_v6,
> >                              struct ovn_port *l3dgw_port,
> > -                            struct lflow_ref *lflow_ref,
> > -                            const struct chassis_features *features)
> > +                            struct lflow_ref *lflow_ref)
> >  {
> >      if (!(nat_entry->type == SNAT || nat_entry->type == DNAT_AND_SNAT))
> {
> >          return;
> > @@ -16019,34 +16018,6 @@ build_lrouter_out_snat_flow(struct lflow_table
> *lflows,
> >                              priority, ds_cstr(match),
> >                              ds_cstr(actions), &nat->header_,
> >                              lflow_ref);
> > -
> > -    /* For the SNAT networks, we need to make sure that connections are
> > -     * properly tracked so we can decide whether to perform SNAT on
> traffic
> > -     * exiting the network. */
> > -    if (features->ct_commit_to_zone && features->ct_next_zone &&
> > -        nat_entry->type == SNAT && !od->is_gw_router) {
> > -        /* For traffic that comes from SNAT network, initiate CT state
> before
> > -         * entering S_ROUTER_OUT_SNAT to allow matching on various CT
> states.
> > -         */
> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 70,
> > -                      ds_cstr(match), "ct_next(snat);",
> > -                      lflow_ref);
> > -
> > -        build_lrouter_out_snat_match(lflows, od, nat, match,
> > -                                     distributed_nat, cidr_bits, is_v6,
> > -                                     l3dgw_port, lflow_ref, true);
> > -
> > -        /* New traffic that goes into SNAT network is committed to CT
> to avoid
> > -         * SNAT-ing replies.*/
> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, priority,
> > -                      ds_cstr(match), "ct_snat;",
> > -                      lflow_ref);
> > -
> > -        ds_put_cstr(match, " && ct.new");
> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_SNAT, priority,
> > -                      ds_cstr(match), "ct_commit_to_zone(snat);",
> > -                      lflow_ref);
> > -    }
> >  }
> >
> >  static void
> > @@ -16439,9 +16410,6 @@ build_lrouter_nat_defrag_and_lb(
> >          ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 50,
> >                        "ip", "flags.loopback = 1; ct_dnat;",
> >                        lflow_ref);
> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 50,
> > -                      "ip && ct.new", "ct_commit { } ; next; ",
> > -                      lflow_ref);
> >      }
> >
> >      /* NAT rules are only valid on Gateway routers and routers with
> > @@ -16459,6 +16427,9 @@ build_lrouter_nat_defrag_and_lb(
> >          !lport_addresses_is_empty(&lrnat_rec->dnat_force_snat_addrs);
> >      bool lb_force_snat_ip =
> >          !lport_addresses_is_empty(&lrnat_rec->lb_force_snat_addrs);
> > +    bool stateful_dnat = lr_stateful_rec->has_lb_vip;
> > +    bool stateful_snat = (dnat_force_snat_ip || lb_force_snat_ip ||
> > +                          lrnat_rec->lb_force_snat_router_ip);
> >
> >      for (size_t i = 0; i < lrnat_rec->n_nat_entries; i++) {
> >          struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i];
> > @@ -16477,6 +16448,21 @@ build_lrouter_nat_defrag_and_lb(
> >              continue;
> >          }
> >
> > +        if (!stateless) {
> > +            switch (nat_entry->type) {
> > +            case DNAT:
> > +                stateful_dnat = true;
> > +                break;
> > +            case SNAT:
> > +                stateful_snat = true;
> > +                break;
> > +            case DNAT_AND_SNAT:
> > +                stateful_snat = true;
> > +                stateful_dnat = true;
> > +                break;
> > +            }
> > +        }
> > +
> >          /* S_ROUTER_IN_UNSNAT
> >           * Ingress UNSNAT table: It is for already established
> connections'
> >           * reverse traffic. i.e., SNAT has already been done in egress
> > @@ -16599,7 +16585,7 @@ build_lrouter_nat_defrag_and_lb(
> >          } else {
> >              build_lrouter_out_snat_flow(lflows, od, nat_entry, match,
> actions,
> >                                          distributed_nat, mac,
> cidr_bits, is_v6,
> > -                                        l3dgw_port, lflow_ref,
> features);
> > +                                        l3dgw_port, lflow_ref);
> >          }
> >
> >          /* S_ROUTER_IN_ADMISSION - S_ROUTER_IN_IP_INPUT */
> > @@ -16689,6 +16675,28 @@ build_lrouter_nat_defrag_and_lb(
> >          }
> >      }
> >
> > +
> > +    bool can_commit = features->ct_commit_to_zone &&
> features->ct_next_zone;
> > +    if (can_commit && stateful_dnat) {
> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 10,
> > +                      "ip && (!ct.trk || !ct.rpl)",
> > +                      "ct_next(dnat);", lflow_ref);
> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 10,
> > +                      "ip && ct.new", "ct_commit_to_zone(dnat);",
> lflow_ref);
> > +    }
> > +
> > +    if (can_commit && stateful_snat) {
> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 10,
> > +                      "ip", "flags.without_unsnat = 1; next;",
> lflow_ref);
> > +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 10,
> > +                      "ip && (!ct.trk || !ct.rpl) && "
> > +                      "flags.without_unsnat == 1", "ct_next(snat);",
> > +                      lflow_ref);
> > +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 10,
> > +                      "ip && ct.new && flags.without_unsnat == 1",
> > +                      "ct_commit_to_zone(snat);", lflow_ref);
> > +    }
> > +
> >      if (use_common_zone && od->nbr->n_nat) {
> >          ds_clear(match);
> >          ds_put_cstr(match, "ip && ct_mark.natted == 1");
> > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > index d6a8c4640..96e28a54a 100644
> > --- a/tests/ovn-northd.at
> > +++ b/tests/ovn-northd.at
> > @@ -1181,18 +1181,18 @@ AT_CAPTURE_FILE([crflows])
> >
> >  AT_CHECK([grep -e "lr_out_snat" drflows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
> == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ip4.src == $allowed_range), action=(ct_snat;)
> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.16.1.1);)
> >  ])
> >
> >  AT_CHECK([grep -e "lr_out_post_snat" drflows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
> == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ip4.src == $allowed_range && ct.new), action=(ct_commit_to_zone(snat);)
> >  ])
> >
> >  AT_CHECK([grep -e "lr_out_snat" crflows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
> == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.16.1.1);)
> >  ])
> > @@ -1220,19 +1220,19 @@ AT_CAPTURE_FILE([crflows2])
> >
> >  AT_CHECK([grep -e "lr_out_snat" drflows2 | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
> == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1")),
> action=(ct_snat;)
> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
> >    table=??(lr_out_snat        ), priority=163  , match=(ip && ip4.src
> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ip4.dst == $disallowed_range), action=(next;)
> >  ])
> >
> >  AT_CHECK([grep -e "lr_out_post_snat" drflows2 | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
> == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ct.new), action=(ct_commit_to_zone(snat);)
> >  ])
> >
> >  AT_CHECK([grep -e "lr_out_snat" crflows2 | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
> == 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
> >    table=??(lr_out_snat        ), priority=35   , match=(ip && ip4.src
> == 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
> > @@ -1259,6 +1259,7 @@ AT_CAPTURE_FILE([crflows2])
> >
> >  AT_CHECK([grep -e "lr_out_snat" drflows3 | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.16.1.2);)
> >  ])
> > @@ -1269,6 +1270,7 @@ AT_CHECK([grep -e "lr_out_post_snat" drflows3 |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep -e "lr_out_snat" crflows3 | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
> == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.16.1.2);)
> >  ])
> > @@ -1294,6 +1296,7 @@ AT_CAPTURE_FILE([crflows2])
> >
> >  AT_CHECK([grep -e "lr_out_snat" drflows4 | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
> >    table=??(lr_out_snat        ), priority=163  , match=(ip && ip4.src
> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ip4.dst == $disallowed_range), action=(next;)
> > @@ -1301,6 +1304,7 @@ AT_CHECK([grep -e "lr_out_snat" drflows4 |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep -e "lr_out_snat" crflows4 | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
> == 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
> >    table=??(lr_out_snat        ), priority=35   , match=(ip && ip4.src
> == 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
> > @@ -1663,6 +1667,7 @@ AT_CAPTURE_FILE([sbflows])
> >  # dnat_and_snat or snat entry.
> >  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
> == 192.168.2.1), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
> == 192.168.2.4), action=(ct_snat;)
> >  ])
> > @@ -1693,6 +1698,7 @@ AT_CAPTURE_FILE([sbflows])
> >  # dnat_and_snat or snat entry.
> >  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
> == 192.168.2.1), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
> == 192.168.2.4), action=(ct_snat;)
> >  ])
> > @@ -1801,6 +1807,7 @@ ovn-nbctl --wait=sb sync
> >
> >  AT_CHECK([ovn-sbctl lflow-list lr0 | grep lr_in_unsnat |
> ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 && ip4.dst
> == 192.168.2.3), action=(ct_snat;)
> >  ])
> >
> > @@ -4277,12 +4284,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.10), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.100), action=(ct_dnat;)
> >  ])
> >
> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(ct_lb_mark(backends=10.0.0.4:8080);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
> action=(ct_lb_mark(backends=10.0.0.40:8080);)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> > @@ -4302,18 +4311,21 @@ AT_CAPTURE_FILE([lr0flows])
> >
> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 && ip4.dst
> == 20.0.0.4), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip6 && ip6.dst
> == aef0::4), action=(ct_snat;)
> >  ])
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.10), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.100), action=(ct_dnat;)
> >  ])
> >
> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
> force_snat);)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> > @@ -4326,6 +4338,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=100  ,
> match=(flags.force_snat_for_lb == 1 && ip4), action=(ct_snat(20.0.0.4);)
> >    table=??(lr_out_snat        ), priority=100  ,
> match=(flags.force_snat_for_lb == 1 && ip6), action=(ct_snat(aef0::4);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> > @@ -4339,7 +4352,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >  ])
> >
> >  check ovn-nbctl --wait=sb set logical_router lr0
> options:lb_force_snat_ip="router_ip"
> > @@ -4352,6 +4365,7 @@ AT_CHECK([grep "lr_in_ip_input" lr0flows | grep
> "priority=60" | sort], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
> > @@ -4359,12 +4373,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.10), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.100), action=(ct_dnat;)
> >  ])
> >
> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
> force_snat);)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> > @@ -4377,6 +4393,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
> action=(ct_snat(172.168.0.100);)
> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
> action=(ct_snat(10.0.0.1);)
> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
> action=(ct_snat(20.0.0.1);)
> > @@ -4391,7 +4408,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >  ])
> >
> >  check ovn-nbctl --wait=sb remove logical_router lr0 options chassis
> > @@ -4416,6 +4433,7 @@ AT_CAPTURE_FILE([lr0flows])
> >
> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
> > @@ -4424,12 +4442,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.10), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.100), action=(ct_dnat;)
> >  ])
> >
> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
> force_snat);)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> > @@ -4442,6 +4462,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
> action=(ct_snat(172.168.0.100);)
> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
> action=(ct_snat(10.0.0.1);)
> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
> action=(ct_snat(20.0.0.1);)
> > @@ -4457,7 +4478,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >  ])
> >
> >  check ovn-nbctl --wait=sb lb-add lb2 10.0.0.20:80 10.0.0.40:8080
> > @@ -4468,6 +4489,7 @@ ovn-sbctl dump-flows lr0 > lr0flows
> >
> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
> > @@ -4476,6 +4498,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.100), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.20), action=(ct_dnat;)
> >  ])
> > @@ -4498,7 +4521,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >  ])
> >
> >  AT_CLEANUP
> > @@ -5744,6 +5767,7 @@ AT_CAPTURE_FILE([lr0flows])
> >
> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
> action=(ct_snat_in_czone;)
> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
> flags.use_snat_zone == 1), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
> action=(ct_snat_in_czone;)
> > @@ -5754,10 +5778,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >  ])
> >
> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
> >  ])
> >
> > @@ -5776,10 +5802,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >  ])
> >
> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src
> == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat_in_czone(172.168.0.10);)
> >    table=??(lr_out_snat        ), priority=154  , match=(ip && ip4.src
> == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl) && reg9[[4]]
> == 1), action=(reg9[[4]] = 0; ct_snat(172.168.0.10);)
> > @@ -5799,6 +5827,7 @@ AT_CAPTURE_FILE([lr0flows])
> >
> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.30 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> > @@ -5806,10 +5835,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >  ])
> >
> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);)
> >  ])
> >
> > @@ -5824,24 +5855,20 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src
> == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_next(snat);)
> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src
> == 10.0.0.10 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_next(snat);)
> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >  ])
> >
> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> > -  table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.dst
> == 10.0.0.0/24 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> >    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src
> == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.168.0.10);)
> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
> == 10.0.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
> == 10.0.0.10 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.168.0.30);)
> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
> == 10.0.0.3 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.168.0.20);)
> >  ])
> >
> >  AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_snat   ), priority=153  , match=(ip && ip4.dst
> == 10.0.0.0/24 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && ct.new),
> action=(ct_commit_to_zone(snat);)
> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
> == 10.0.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && ct.new),
> action=(ct_commit_to_zone(snat);)
> >  ])
> >
> >  # Associate load balancer to lr0
> > @@ -5866,6 +5893,7 @@ AT_CAPTURE_FILE([lr0flows])
> >
> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
> action=(ct_snat_in_czone;)
> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
> flags.use_snat_zone == 1), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
> action=(ct_snat_in_czone;)
> > @@ -5876,6 +5904,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.10), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.100), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.200), action=(ct_dnat;)
> > @@ -5884,6 +5913,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.0.200 &&
> is_chassis_resident("cr-lr0-public")),
> action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 &&
> is_chassis_resident("cr-lr0-public")),
> action=(ct_lb_mark(backends=10.0.0.4:8080);)
> > @@ -5916,10 +5946,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >  ])
> >
> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src
> == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat_in_czone(172.168.0.10);)
> >    table=??(lr_out_snat        ), priority=154  , match=(ip && ip4.src
> == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl) && reg9[[4]]
> == 1), action=(reg9[[4]] = 0; ct_snat(172.168.0.10);)
> > @@ -5939,6 +5971,7 @@ AT_CAPTURE_FILE([lr0flows])
> >
> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.30 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> > @@ -5946,6 +5979,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.10), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.100), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.200), action=(ct_dnat;)
> > @@ -5954,6 +5988,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);)
> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.0.200 &&
> is_chassis_resident("cr-lr0-public")),
> action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 &&
> is_chassis_resident("cr-lr0-public")),
> action=(ct_lb_mark(backends=10.0.0.4:8080);)
> > @@ -5982,24 +6017,20 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src
> == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_next(snat);)
> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src
> == 10.0.0.10 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_next(snat);)
> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >  ])
> >
> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> > -  table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.dst
> == 10.0.0.0/24 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> >    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src
> == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.168.0.10);)
> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
> == 10.0.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
> == 10.0.0.10 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.168.0.30);)
> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
> == 10.0.0.3 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.168.0.20);)
> >  ])
> >
> >  AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_snat   ), priority=153  , match=(ip && ip4.dst
> == 10.0.0.0/24 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && ct.new),
> action=(ct_commit_to_zone(snat);)
> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
> == 10.0.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && ct.new),
> action=(ct_commit_to_zone(snat);)
> >  ])
> >
> >  # Make the logical router as Gateway router
> > @@ -6013,6 +6044,7 @@ AT_CAPTURE_FILE([lr0flows])
> >
> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
> == 172.168.0.10), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
> == 172.168.0.20), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
> == 172.168.0.30), action=(ct_snat;)
> > @@ -6020,6 +6052,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.10), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.100), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.200), action=(ct_dnat;)
> > @@ -6028,6 +6061,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.0.200),
> action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(ct_lb_mark(backends=10.0.0.4:8080);)
> > @@ -6053,11 +6087,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >  ])
> >
> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >    table=??(lr_out_snat        ), priority=25   , match=(ip && ip4.src
> == 10.0.0.0/24 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);)
> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
> == 10.0.0.10 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.30);)
> > @@ -6074,6 +6109,7 @@ AT_CAPTURE_FILE([lr0flows])
> >
> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
> == 172.168.0.10), action=(ct_snat;)
> > @@ -6083,6 +6119,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.10), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.100), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.200), action=(ct_dnat;)
> > @@ -6091,6 +6128,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.0.200),
> action=(flags.force_snat_for_lb = 1;
> ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
> > @@ -6116,11 +6154,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >  ])
> >
> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
> action=(ct_snat(172.168.0.10);)
> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
> action=(ct_snat(10.0.0.1);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> > @@ -6138,6 +6177,7 @@ AT_CAPTURE_FILE([lr0flows])
> >
> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
> == 172.168.0.10), action=(ct_snat;)
> > @@ -6147,6 +6187,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.10), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.10), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.100), action=(ct_dnat;)
> > @@ -6156,6 +6197,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.0.200),
> action=(flags.force_snat_for_lb = 1;
> ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
> > @@ -6182,11 +6224,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >  ])
> >
> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
> action=(ct_snat(172.168.0.10);)
> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
> action=(ct_snat(10.0.0.1);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> > @@ -6212,6 +6255,7 @@ AT_CAPTURE_FILE([lr0flows])
> >
> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip6.dst == def0::10), action=(ct_snat;)
> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
> > @@ -6223,6 +6267,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 10.0.0.10), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.10), action=(ct_dnat;)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.100), action=(ct_dnat;)
> > @@ -6233,6 +6278,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.0.200),
> action=(flags.force_snat_for_lb = 1;
> ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
> > @@ -6260,11 +6306,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >  ])
> >
> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
> action=(ct_snat(172.168.0.10);)
> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
> action=(ct_snat(10.0.0.1);)
> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip6 && outport == "lr0-public"),
> action=(ct_snat(def0::10);)
> > @@ -6291,15 +6338,18 @@ AT_CAPTURE_FILE([lr0flows])
> >
> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >  ])
> >
> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl)), action=(ct_next(dnat);)
> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
> == 172.168.0.210), action=(ct_dnat;)
> >  ])
> >
> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.0.210 && tcp && tcp.dst == 60),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.50:6062,
> 10.0.0.60:6062; force_snat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.0.210 && udp && udp.dst == 60),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.50:6062,
> 10.0.0.60:6062; force_snat);)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> > @@ -6322,11 +6372,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
> action=(ct_commit { } ; next; )
> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >  ])
> >
> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new &&
> flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >  ])
> >
> > @@ -6358,6 +6409,7 @@ check ovn-nbctl --wait=sb sync
> >
> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
> ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.10.10), action=(reg0 = 0; reject {
> outport <-> inport; next(pipeline=egress,table=??);};)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
> !ct.est && !ct.new), action=(ct_commit_nat;)
> > @@ -6372,6 +6424,7 @@ check ovn-nbctl --wait=sb set load_balancer lb5
> options:skip_snat=true
> >
> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
> ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.10.10), action=(flags.skip_snat_for_lb
> = 1; reg0 = 0; reject { outport <-> inport;
> next(pipeline=egress,table=??);};)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
> !ct.est && !ct.new), action=(ct_commit_nat;)
> > @@ -6388,6 +6441,7 @@ check ovn-nbctl --wait=sb set logical_router lr0
> options:lb_force_snat_ip="route
> >
> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
> ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.10.10),
> action=(flags.force_snat_for_lb = 1; reg0 = 0; reject { outport <-> inport;
> next(pipeline=egress,table=??);};)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
> !ct.est && !ct.new), action=(ct_commit_nat;)
> > @@ -6405,6 +6459,7 @@ check ovn-nbctl --wait=sb lr-lb-add lr0 lb6
> >
> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
> ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.10.30), action=(drop;)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
> !ct.est && !ct.new), action=(ct_commit_nat;)
> > @@ -6419,6 +6474,7 @@ check ovn-nbctl --wait=sb set load_balancer lb6
> options:skip_snat=true
> >
> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
> ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.10.30), action=(flags.skip_snat_for_lb
> = 1; drop;)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
> !ct.est && !ct.new), action=(ct_commit_nat;)
> > @@ -6435,6 +6491,7 @@ check ovn-nbctl --wait=sb set logical_router lr0
> options:lb_force_snat_ip="route
> >
> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
> ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.10.30),
> action=(flags.force_snat_for_lb = 1; drop;)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
> !ct.est && !ct.new), action=(ct_commit_nat;)
> > @@ -7951,9 +8008,6 @@ AT_CHECK([grep lr_in_unsnat lrflows | grep ct_snat
> | ovn_strip_lflows], [0], [dn
> >  ])
> >
> >  AT_CHECK([grep lr_out_snat lrflows | grep ct_snat | ovn_strip_lflows],
> [0], [dnl
> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
> == 20.0.0.10 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1")),
> action=(ct_snat;)
> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
> == 20.0.0.10 && inport == "DR-S2" && is_chassis_resident("cr-DR-S2")),
> action=(ct_snat;)
> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
> == 20.0.0.10 && inport == "DR-S3" && is_chassis_resident("cr-DR-S3")),
> action=(ct_snat;)
> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
> == 20.0.0.10 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.10);)
> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
> == 20.0.0.10 && outport == "DR-S2" && is_chassis_resident("cr-DR-S2") &&
> (!ct.trk || !ct.rpl)), action=(ct_snat(10.0.0.10);)
> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
> == 20.0.0.10 && outport == "DR-S3" && is_chassis_resident("cr-DR-S3") &&
> (!ct.trk || !ct.rpl)), action=(ct_snat(192.168.0.10);)
> > @@ -7961,9 +8015,6 @@ AT_CHECK([grep lr_out_snat lrflows | grep ct_snat
> | ovn_strip_lflows], [0], [dnl
> >
> >  AT_CHECK([grep lr_out_post_snat lrflows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
> action=(next;)
> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
> == 20.0.0.10 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
> ct.new), action=(ct_commit_to_zone(snat);)
> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
> == 20.0.0.10 && inport == "DR-S2" && is_chassis_resident("cr-DR-S2") &&
> ct.new), action=(ct_commit_to_zone(snat);)
> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
> == 20.0.0.10 && inport == "DR-S3" && is_chassis_resident("cr-DR-S3") &&
> ct.new), action=(ct_commit_to_zone(snat);)
> >  ])
> >
> >  check ovn-nbctl --wait=sb lr-nat-del DR snat 20.0.0.10
> > @@ -9387,6 +9438,7 @@ AT_CHECK([grep "lr_in_lb_aff_check" R1flows |
> ovn_strip_lflows], [0], [dnl
> >  ])
> >  AT_CHECK([grep "lr_in_dnat " R1flows | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
> action=(ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);)
> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
> && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
> && ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 && reg8[[0..15]] ==
> 80), action=(reg0 = 172.16.0.10; ct_lb_mark(backends=20.0.0.2:80);)
> > @@ -9411,6 +9463,7 @@ AT_CAPTURE_FILE([R1flows_skip_snat])
> >
> >  AT_CHECK([grep "lr_in_dnat " R1flows_skip_snat | ovn_strip_lflows],
> [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
> action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
> 20.0.0.2:80; skip_snat);)
> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
> && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
> && ct.new && ip4.dst == 172.16.0.10 && 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);)
> > @@ -9432,6 +9485,7 @@ AT_CAPTURE_FILE([R1flows_force_snat])
> >
> >  AT_CHECK([grep "lr_in_dnat " R1flows_force_snat | ovn_strip_lflows],
> [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
> 20.0.0.2:80; force_snat);)
> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
> && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
> && ct.new && ip4.dst == 172.16.0.10 && 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);)
> > @@ -9452,6 +9506,7 @@ AT_CAPTURE_FILE([R1flows_force_skip_snat])
> >
> >  AT_CHECK([grep "lr_in_dnat " R1flows_force_skip_snat |
> ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
> action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
> 20.0.0.2:80; skip_snat);)
> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
> && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
> && ct.new && ip4.dst == 172.16.0.10 && 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);)
> > @@ -9476,6 +9531,7 @@ AT_CAPTURE_FILE([R1flows_2lbs])
> >
> >  AT_CHECK([grep "lr_in_dnat " R1flows_2lbs | ovn_strip_lflows], [0], [dnl
> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
> action=(ct_commit_to_zone(dnat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
> action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
> 20.0.0.2:80; skip_snat);)
> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.16.0.20 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
> 20.0.0.2:80; force_snat);)
> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
> && ct.new && ip4.dst == 172.16.0.10 && 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);)
> > diff --git a/tests/system-ovn.at b/tests/system-ovn.at
> > index 861b1cb99..dc7b0ab2e 100644
> > --- a/tests/system-ovn.at
> > +++ b/tests/system-ovn.at
> > @@ -117,6 +117,7 @@ NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2
> 30.0.0.2 | FORMAT_PING], \
> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.2) | \
> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> >
>  icmp,orig=(src=172.16.1.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
> >
> +icmp,orig=(src=172.16.1.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
> >
>  icmp,orig=(src=172.16.1.2,dst=30.0.0.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
> >  ])
> >
> > @@ -297,6 +298,7 @@ NS_CHECK_EXEC([alice1], [ping6 -q -c 3 -i 0.3 -w 2
> fd30::2 | FORMAT_PING], \
> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd21::2) | \
> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> >
>  icmpv6,orig=(src=fd21::2,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
> >
> +icmpv6,orig=(src=fd21::2,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
> >
>  icmpv6,orig=(src=fd21::2,dst=fd30::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
> >  ])
> >
> > @@ -3753,6 +3755,7 @@ NS_CHECK_EXEC([foo2], [ping6 -q -c 3 -i 0.3 -w 2
> fd20::2 | FORMAT_PING], \
> >  ovs-appctl dpctl/dump-conntrack | grep icmpv6
> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd11::3) | \
> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> >
> +icmpv6,orig=(src=fd11::3,dst=fd20::2,id=<cleared>,type=128,code=0),reply=(src=fd20::2,dst=fd11::3,id=<cleared>,type=129,code=0),zone=<cleared>
> >  ])
> >
> >  # We verify that SNAT indeed happened via 'dump-conntrack' command.
> > @@ -3938,6 +3941,8 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2
> 192.168.2.2 | FORMAT_PING], \
> >  # We verify that the connection is not tracked.
> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(192.168.2.2) | \
> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> >
> +icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
> >
> +icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
> >  ])
> >
> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > @@ -3950,6 +3955,8 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
> 192.168.2.2 | FORMAT_PING], \
> >  # We verify that the connection is not tracked.
> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(192.168.2.2) | \
> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> >
> +icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
> >
> +icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
> >  ])
> >
> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > @@ -3959,9 +3966,11 @@ NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2
> 192.168.1.3 | FORMAT_PING], \
> >  3 packets transmitted, 3 received, 0% packet loss, time 0ms
> >  ])
> >
> > -# We verify that the connection is not tracked.
> > +# We verify that the connection is tracked.
> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(192.168.2.2) | \
> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> >
> +icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
> >
> +icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
> >  ])
> >
> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > @@ -3978,6 +3987,7 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep
> icmp | FORMAT_CT(172.16.1.4) |
> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> >
>  icmp,orig=(src=172.16.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
> >
>  icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
> >
> +icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
> >  ])
> >
> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > @@ -3993,7 +4003,6 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
> 172.16.1.4 | FORMAT_PING], \
> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(172.16.1.1) | \
> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> >
>  icmp,orig=(src=172.16.1.1,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
> >
> -icmp,orig=(src=172.16.1.1,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
> >
>  icmp,orig=(src=192.168.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
> >  ])
> >
> > @@ -4144,6 +4153,7 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2
> fd20::4 | FORMAT_PING], \
> >  # Then DNAT of 'bar1' address happens (listed first below).
> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::4) | \
> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> >
> +icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd11::2,id=<cleared>,type=129,code=0),zone=<cleared>
> >
>  icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
> >
>  icmpv6,orig=(src=fd20::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
> >  ])
> > @@ -4161,7 +4171,6 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
> fd20::4 | FORMAT_PING], \
> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \
> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> >
>  icmpv6,orig=(src=fd11::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
> >
> -icmpv6,orig=(src=fd20::1,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
> >
>  icmpv6,orig=(src=fd20::1,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
> >  ])
> >
> > @@ -8682,10 +8691,10 @@ test_ping sw11 192.168.1.2
> >  OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep -v "n_packets=0" |
> grep 'nat(src=172.16.1.21)'])
> >  # Ensure conntrack entry is present
> >  OVS_WAIT_FOR_OUTPUT([
> > -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
> > +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
> >        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> >
> -icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
> >
> -tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
> >
> +icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
> >
> +tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
> >  ])
> >
> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > @@ -8697,9 +8706,11 @@ test_ping sw11 192.168.1.2
> >
> >  # Ensure conntrack entry is present
> >  OVS_WAIT_FOR_OUTPUT([
> > -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
> > +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
> >        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> >
> +icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
> >
>  icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
> >
> +tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
> >
>  tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
> >  ])
> >
> > @@ -8711,10 +8722,10 @@ test_ping sw11 172.16.1.2
> >
> >  # Ensure conntrack entry is present
> >  OVS_WAIT_FOR_OUTPUT([
> > -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
> > +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
> >        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> >
> -icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
> >
> -tcp,orig=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
> >
> +icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
> >
> +tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
> >  ])
> >
> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > --
> > 2.46.2
> >
> > _______________________________________________
> > dev mailing list
> > dev@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Han Zhou Nov. 14, 2024, 7:35 a.m. UTC | #4
On Wed, Nov 13, 2024 at 2:00 AM Ales Musil <amusil@redhat.com> wrote:

>
>
> On Wed, Nov 13, 2024 at 10:37 AM Han Zhou <hzhou@ovn.org> wrote:
>
>> Thanks Ales.
>>
>>
> Hi Han,
>
>
>>
>> On Tue, Oct 8, 2024 at 3:12 AM Ales Musil <amusil@redhat.com> wrote:
>> >
>> > Commit all traffic that is not already commit by either NAT or LB. This
>> > ensures that the traffic is tracked, and we don't erroneously commit
>> > reply traffic, or reply traffic is not marked as invalid.
>> >
>> > To achieve the commit we need to perform lookup on every packet
>> > that goes through LR pipeline whenever there is stateful NAT.
>> >
>> > The SNAT lookup requires additional flag as the unSNAT is happening
>> > in ingress pipeline and at that point we need to know if the packet
>> > is reply or not. This is not required for DNAT, because unDNAT stage
>> > happens in egress.
>>
>> Could you help explain in a little more detail how the ct.rpl relates to
>> this new without_unsnat flag? I still can't figure out why this flag is
>> needed.
>>
>
> Without it we would commit reply traffic for SNAT which is wrong. That
> caused all sorts of issues.
>
>
I understand that we shouldn't commit reply traffic, but my question was
why does the "without_unsnat" flag avoid that. I don't see any link between
the without_unsnat and ct.rpl. It may be helpful to add comments in the
code as well.


>
>> In addition, it seems that with this flag, you commit to snat zone only
>> if the packet's without_unsnat == 1, but even if the packet did perform
>> unSNAT we would still want to commit it to SNAT zone, because of the HW
>> offload issue reported here:
>> https://mail.openvswitch.org/pipermail/ovs-dev/2024-August/417025.html
>>
>
> I'm probably missing something, why would we want to commit traffic that
> did unSNAT? That traffic is already committed in the SNAT zone. The issue
> described in the thread should be gone, AFAIU the problem was that the
> traffic wasn't committed in the original direction, then unSNAT would mark
> it as new. That shouldn't happen with this series.
>
>
My understanding was, the lflow you added with priority 10 sets the
without_unsnat flag for the packets that didn't perform ct_snat at the
UNSNAT stage, and later you commit these to the SNAT zone. However, for the
other packets that performed the ct_snat at the UNSNAT stage, having
traversed the SNAT zone doesn't necessarily mean they were committed to the
zone, right? So with your change, if a packet was sent to the CT at the
UNSNAT stage, it would never get committed to SNAT zone, if not committed
at any other stages. Did I miss anything?

Thanks,
Han


>
>> BTW, I have been trying my best but I feel it is quite difficult to
>> reason about the correctness of the DNAT/SNAT pipelines, which have become
>> so complex. I hope our test case coverage is sufficient for all kinds of
>> corner cases :)
>>
>
> Yeah I understand and we probably have decent coverage because some of my
> thoughts were quickly disputed by failing and misbehaving tests :)
>
>>
>> I am still working on performance test for this patch. Sorry it took so
>> long but I still need some more time.
>>
>>
> This is one of the things that we shouldn't rush, thank you for looking
> into the performance testing!
>
>
>>
>> Regards,
>> Han
>>
>>
> Regards,
> Ales
>
>
>> >
>> > Signed-off-by: Ales Musil <amusil@redhat.com>
>> > ---
>> > There is one failing system test with userspace datapath, that's due
>> > to the recirculation limit that is being hit due to additional
>> > lookups.
>> > ---
>> >  include/ovn/logical-fields.h |   4 ++
>> >  lib/logical-fields.c         |   4 ++
>> >  northd/northd.c              |  76 ++++++++++++----------
>> >  tests/ovn-northd.at          | 118 ++++++++++++++++++++++++++---------
>> >  tests/system-ovn.at          |  31 ++++++---
>> >  5 files changed, 158 insertions(+), 75 deletions(-)
>> >
>> > diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
>> > index d6c4a9b6b..cc1f50ff2 100644
>> > --- a/include/ovn/logical-fields.h
>> > +++ b/include/ovn/logical-fields.h
>> > @@ -82,6 +82,7 @@ enum mff_log_flags_bits {
>> >      MLF_LOCALNET_BIT = 15,
>> >      MLF_RX_FROM_TUNNEL_BIT = 16,
>> >      MLF_ICMP_SNAT_BIT = 17,
>> > +    MLF_WITHOUT_UNSNAT_BIT = 18,
>> >  };
>> >
>> >  /* MFF_LOG_FLAGS_REG flag assignments */
>> > @@ -137,6 +138,9 @@ enum mff_log_flags {
>> >      MLF_RX_FROM_TUNNEL = (1 << MLF_RX_FROM_TUNNEL_BIT),
>> >
>> >      MLF_ICMP_SNAT = (1 << MLF_ICMP_SNAT_BIT),
>> > +
>> > +    /* Indicate that the packet didn't go through unSNAT. */
>> > +    MLF_WITHOUT_UNSNAT = (1 << MLF_WITHOUT_UNSNAT_BIT),
>> >  };
>> >
>> >  /* OVN logical fields
>> > diff --git a/lib/logical-fields.c b/lib/logical-fields.c
>> > index 5a8b53f2b..c63e19897 100644
>> > --- a/lib/logical-fields.c
>> > +++ b/lib/logical-fields.c
>> > @@ -139,6 +139,10 @@ ovn_init_symtab(struct shash *symtab)
>> >                               flags_str);
>> >      snprintf(flags_str, sizeof flags_str, "flags[%d]",
>> MLF_RX_FROM_TUNNEL_BIT);
>> >      expr_symtab_add_subfield(symtab, "flags.tunnel_rx", NULL,
>> flags_str);
>> > +    snprintf(flags_str, sizeof flags_str, "flags[%d]",
>> > +             MLF_WITHOUT_UNSNAT_BIT);
>> > +    expr_symtab_add_subfield(symtab, "flags.without_unsnat", NULL,
>> > +                             flags_str);
>> >
>> >      /* Connection tracking state. */
>> >      expr_symtab_add_field_scoped(symtab, "ct_mark", MFF_CT_MARK, NULL,
>> false,
>> > diff --git a/northd/northd.c b/northd/northd.c
>> > index 0364dd766..a42057e45 100644
>> > --- a/northd/northd.c
>> > +++ b/northd/northd.c
>> > @@ -15987,8 +15987,7 @@ build_lrouter_out_snat_flow(struct lflow_table
>> *lflows,
>> >                              struct ds *actions, bool distributed_nat,
>> >                              struct eth_addr mac, int cidr_bits, bool
>> is_v6,
>> >                              struct ovn_port *l3dgw_port,
>> > -                            struct lflow_ref *lflow_ref,
>> > -                            const struct chassis_features *features)
>> > +                            struct lflow_ref *lflow_ref)
>> >  {
>> >      if (!(nat_entry->type == SNAT || nat_entry->type ==
>> DNAT_AND_SNAT)) {
>> >          return;
>> > @@ -16019,34 +16018,6 @@ build_lrouter_out_snat_flow(struct lflow_table
>> *lflows,
>> >                              priority, ds_cstr(match),
>> >                              ds_cstr(actions), &nat->header_,
>> >                              lflow_ref);
>> > -
>> > -    /* For the SNAT networks, we need to make sure that connections are
>> > -     * properly tracked so we can decide whether to perform SNAT on
>> traffic
>> > -     * exiting the network. */
>> > -    if (features->ct_commit_to_zone && features->ct_next_zone &&
>> > -        nat_entry->type == SNAT && !od->is_gw_router) {
>> > -        /* For traffic that comes from SNAT network, initiate CT state
>> before
>> > -         * entering S_ROUTER_OUT_SNAT to allow matching on various CT
>> states.
>> > -         */
>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 70,
>> > -                      ds_cstr(match), "ct_next(snat);",
>> > -                      lflow_ref);
>> > -
>> > -        build_lrouter_out_snat_match(lflows, od, nat, match,
>> > -                                     distributed_nat, cidr_bits, is_v6,
>> > -                                     l3dgw_port, lflow_ref, true);
>> > -
>> > -        /* New traffic that goes into SNAT network is committed to CT
>> to avoid
>> > -         * SNAT-ing replies.*/
>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, priority,
>> > -                      ds_cstr(match), "ct_snat;",
>> > -                      lflow_ref);
>> > -
>> > -        ds_put_cstr(match, " && ct.new");
>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_SNAT, priority,
>> > -                      ds_cstr(match), "ct_commit_to_zone(snat);",
>> > -                      lflow_ref);
>> > -    }
>> >  }
>> >
>> >  static void
>> > @@ -16439,9 +16410,6 @@ build_lrouter_nat_defrag_and_lb(
>> >          ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 50,
>> >                        "ip", "flags.loopback = 1; ct_dnat;",
>> >                        lflow_ref);
>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 50,
>> > -                      "ip && ct.new", "ct_commit { } ; next; ",
>> > -                      lflow_ref);
>> >      }
>> >
>> >      /* NAT rules are only valid on Gateway routers and routers with
>> > @@ -16459,6 +16427,9 @@ build_lrouter_nat_defrag_and_lb(
>> >          !lport_addresses_is_empty(&lrnat_rec->dnat_force_snat_addrs);
>> >      bool lb_force_snat_ip =
>> >          !lport_addresses_is_empty(&lrnat_rec->lb_force_snat_addrs);
>> > +    bool stateful_dnat = lr_stateful_rec->has_lb_vip;
>> > +    bool stateful_snat = (dnat_force_snat_ip || lb_force_snat_ip ||
>> > +                          lrnat_rec->lb_force_snat_router_ip);
>> >
>> >      for (size_t i = 0; i < lrnat_rec->n_nat_entries; i++) {
>> >          struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i];
>> > @@ -16477,6 +16448,21 @@ build_lrouter_nat_defrag_and_lb(
>> >              continue;
>> >          }
>> >
>> > +        if (!stateless) {
>> > +            switch (nat_entry->type) {
>> > +            case DNAT:
>> > +                stateful_dnat = true;
>> > +                break;
>> > +            case SNAT:
>> > +                stateful_snat = true;
>> > +                break;
>> > +            case DNAT_AND_SNAT:
>> > +                stateful_snat = true;
>> > +                stateful_dnat = true;
>> > +                break;
>> > +            }
>> > +        }
>> > +
>> >          /* S_ROUTER_IN_UNSNAT
>> >           * Ingress UNSNAT table: It is for already established
>> connections'
>> >           * reverse traffic. i.e., SNAT has already been done in egress
>> > @@ -16599,7 +16585,7 @@ build_lrouter_nat_defrag_and_lb(
>> >          } else {
>> >              build_lrouter_out_snat_flow(lflows, od, nat_entry, match,
>> actions,
>> >                                          distributed_nat, mac,
>> cidr_bits, is_v6,
>> > -                                        l3dgw_port, lflow_ref,
>> features);
>> > +                                        l3dgw_port, lflow_ref);
>> >          }
>> >
>> >          /* S_ROUTER_IN_ADMISSION - S_ROUTER_IN_IP_INPUT */
>> > @@ -16689,6 +16675,28 @@ build_lrouter_nat_defrag_and_lb(
>> >          }
>> >      }
>> >
>> > +
>> > +    bool can_commit = features->ct_commit_to_zone &&
>> features->ct_next_zone;
>> > +    if (can_commit && stateful_dnat) {
>> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 10,
>> > +                      "ip && (!ct.trk || !ct.rpl)",
>> > +                      "ct_next(dnat);", lflow_ref);
>> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 10,
>> > +                      "ip && ct.new", "ct_commit_to_zone(dnat);",
>> lflow_ref);
>> > +    }
>> > +
>> > +    if (can_commit && stateful_snat) {
>> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 10,
>> > +                      "ip", "flags.without_unsnat = 1; next;",
>> lflow_ref);
>> > +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 10,
>> > +                      "ip && (!ct.trk || !ct.rpl) && "
>> > +                      "flags.without_unsnat == 1", "ct_next(snat);",
>> > +                      lflow_ref);
>> > +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 10,
>> > +                      "ip && ct.new && flags.without_unsnat == 1",
>> > +                      "ct_commit_to_zone(snat);", lflow_ref);
>> > +    }
>> > +
>> >      if (use_common_zone && od->nbr->n_nat) {
>> >          ds_clear(match);
>> >          ds_put_cstr(match, "ip && ct_mark.natted == 1");
>> > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>> > index d6a8c4640..96e28a54a 100644
>> > --- a/tests/ovn-northd.at
>> > +++ b/tests/ovn-northd.at
>> > @@ -1181,18 +1181,18 @@ AT_CAPTURE_FILE([crflows])
>> >
>> >  AT_CHECK([grep -e "lr_out_snat" drflows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>> == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>> ip4.src == $allowed_range), action=(ct_snat;)
>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>> ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
>> action=(ct_snat(172.16.1.1);)
>> >  ])
>> >
>> >  AT_CHECK([grep -e "lr_out_post_snat" drflows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>> == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>> ip4.src == $allowed_range && ct.new), action=(ct_commit_to_zone(snat);)
>> >  ])
>> >
>> >  AT_CHECK([grep -e "lr_out_snat" crflows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
>> == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
>> action=(ct_snat(172.16.1.1);)
>> >  ])
>> > @@ -1220,19 +1220,19 @@ AT_CAPTURE_FILE([crflows2])
>> >
>> >  AT_CHECK([grep -e "lr_out_snat" drflows2 | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>> == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1")),
>> action=(ct_snat;)
>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>> (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
>> >    table=??(lr_out_snat        ), priority=163  , match=(ip && ip4.src
>> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>> ip4.dst == $disallowed_range), action=(next;)
>> >  ])
>> >
>> >  AT_CHECK([grep -e "lr_out_post_snat" drflows2 | ovn_strip_lflows],
>> [0], [dnl
>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>> == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>> ct.new), action=(ct_commit_to_zone(snat);)
>> >  ])
>> >
>> >  AT_CHECK([grep -e "lr_out_snat" crflows2 | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
>> == 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
>> >    table=??(lr_out_snat        ), priority=35   , match=(ip && ip4.src
>> == 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
>> > @@ -1259,6 +1259,7 @@ AT_CAPTURE_FILE([crflows2])
>> >
>> >  AT_CHECK([grep -e "lr_out_snat" drflows3 | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>> ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
>> action=(ct_snat(172.16.1.2);)
>> >  ])
>> > @@ -1269,6 +1270,7 @@ AT_CHECK([grep -e "lr_out_post_snat" drflows3 |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep -e "lr_out_snat" crflows3 | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
>> == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
>> action=(ct_snat(172.16.1.2);)
>> >  ])
>> > @@ -1294,6 +1296,7 @@ AT_CAPTURE_FILE([crflows2])
>> >
>> >  AT_CHECK([grep -e "lr_out_snat" drflows4 | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>> (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
>> >    table=??(lr_out_snat        ), priority=163  , match=(ip && ip4.src
>> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>> ip4.dst == $disallowed_range), action=(next;)
>> > @@ -1301,6 +1304,7 @@ AT_CHECK([grep -e "lr_out_snat" drflows4 |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep -e "lr_out_snat" crflows4 | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
>> == 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
>> >    table=??(lr_out_snat        ), priority=35   , match=(ip && ip4.src
>> == 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
>> > @@ -1663,6 +1667,7 @@ AT_CAPTURE_FILE([sbflows])
>> >  # dnat_and_snat or snat entry.
>> >  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>> == 192.168.2.1), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>> == 192.168.2.4), action=(ct_snat;)
>> >  ])
>> > @@ -1693,6 +1698,7 @@ AT_CAPTURE_FILE([sbflows])
>> >  # dnat_and_snat or snat entry.
>> >  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>> == 192.168.2.1), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>> == 192.168.2.4), action=(ct_snat;)
>> >  ])
>> > @@ -1801,6 +1807,7 @@ ovn-nbctl --wait=sb sync
>> >
>> >  AT_CHECK([ovn-sbctl lflow-list lr0 | grep lr_in_unsnat |
>> ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 && ip4.dst
>> == 192.168.2.3), action=(ct_snat;)
>> >  ])
>> >
>> > @@ -4277,12 +4284,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.10), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.100), action=(ct_dnat;)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>> action=(ct_lb_mark(backends=10.0.0.4:8080);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
>> action=(ct_lb_mark(backends=10.0.0.40:8080);)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>> > @@ -4302,18 +4311,21 @@ AT_CAPTURE_FILE([lr0flows])
>> >
>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 && ip4.dst
>> == 20.0.0.4), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip6 && ip6.dst
>> == aef0::4), action=(ct_snat;)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.10), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.100), action=(ct_dnat;)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
>> force_snat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
>> force_snat);)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>> > @@ -4326,6 +4338,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=100  ,
>> match=(flags.force_snat_for_lb == 1 && ip4), action=(ct_snat(20.0.0.4);)
>> >    table=??(lr_out_snat        ), priority=100  ,
>> match=(flags.force_snat_for_lb == 1 && ip6), action=(ct_snat(aef0::4);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> > @@ -4339,7 +4352,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
>> action=(ct_commit { } ; next; )
>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>> >  ])
>> >
>> >  check ovn-nbctl --wait=sb set logical_router lr0
>> options:lb_force_snat_ip="router_ip"
>> > @@ -4352,6 +4365,7 @@ AT_CHECK([grep "lr_in_ip_input" lr0flows | grep
>> "priority=60" | sort], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
>> > @@ -4359,12 +4373,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.10), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.100), action=(ct_dnat;)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
>> force_snat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
>> force_snat);)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>> > @@ -4377,6 +4393,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=110  ,
>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
>> action=(ct_snat(172.168.0.100);)
>> >    table=??(lr_out_snat        ), priority=110  ,
>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
>> action=(ct_snat(10.0.0.1);)
>> >    table=??(lr_out_snat        ), priority=110  ,
>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
>> action=(ct_snat(20.0.0.1);)
>> > @@ -4391,7 +4408,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
>> action=(ct_commit { } ; next; )
>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>> >  ])
>> >
>> >  check ovn-nbctl --wait=sb remove logical_router lr0 options chassis
>> > @@ -4416,6 +4433,7 @@ AT_CAPTURE_FILE([lr0flows])
>> >
>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
>> > @@ -4424,12 +4442,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.10), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.100), action=(ct_dnat;)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
>> force_snat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
>> force_snat);)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>> > @@ -4442,6 +4462,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=110  ,
>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
>> action=(ct_snat(172.168.0.100);)
>> >    table=??(lr_out_snat        ), priority=110  ,
>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
>> action=(ct_snat(10.0.0.1);)
>> >    table=??(lr_out_snat        ), priority=110  ,
>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
>> action=(ct_snat(20.0.0.1);)
>> > @@ -4457,7 +4478,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
>> action=(ct_commit { } ; next; )
>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>> >  ])
>> >
>> >  check ovn-nbctl --wait=sb lb-add lb2 10.0.0.20:80 10.0.0.40:8080
>> > @@ -4468,6 +4489,7 @@ ovn-sbctl dump-flows lr0 > lr0flows
>> >
>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
>> > @@ -4476,6 +4498,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.100), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.20), action=(ct_dnat;)
>> >  ])
>> > @@ -4498,7 +4521,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
>> action=(ct_commit { } ; next; )
>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>> >  ])
>> >
>> >  AT_CLEANUP
>> > @@ -5744,6 +5767,7 @@ AT_CAPTURE_FILE([lr0flows])
>> >
>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.10 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
>> action=(ct_snat_in_czone;)
>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.10 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
>> flags.use_snat_zone == 1), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.20 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
>> action=(ct_snat_in_czone;)
>> > @@ -5754,10 +5778,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.20 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
>> >  ])
>> >
>> > @@ -5776,10 +5802,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> >    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src
>> == 10.0.0.0/24 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>> action=(ct_snat_in_czone(172.168.0.10);)
>> >    table=??(lr_out_snat        ), priority=154  , match=(ip && ip4.src
>> == 10.0.0.0/24 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl) && reg9[[4]]
>> == 1), action=(reg9[[4]] = 0; ct_snat(172.168.0.10);)
>> > @@ -5799,6 +5827,7 @@ AT_CAPTURE_FILE([lr0flows])
>> >
>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.10 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.20 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.30 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>> > @@ -5806,10 +5835,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.20 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);)
>> >  ])
>> >
>> > @@ -5824,24 +5855,20 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src
>> == 10.0.0.0/24 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>> action=(ct_next(snat);)
>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src
>> == 10.0.0.10 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>> action=(ct_next(snat);)
>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> > -  table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.dst
>> == 10.0.0.0/24 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>> >    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src
>> == 10.0.0.0/24 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>> action=(ct_snat(172.168.0.10);)
>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>> == 10.0.0.10 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>> == 10.0.0.10 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>> action=(ct_snat(172.168.0.30);)
>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>> == 10.0.0.3 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>> action=(ct_snat(172.168.0.20);)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_snat   ), priority=153  , match=(ip && ip4.dst
>> == 10.0.0.0/24 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && ct.new),
>> action=(ct_commit_to_zone(snat);)
>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>> == 10.0.0.10 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && ct.new),
>> action=(ct_commit_to_zone(snat);)
>> >  ])
>> >
>> >  # Associate load balancer to lr0
>> > @@ -5866,6 +5893,7 @@ AT_CAPTURE_FILE([lr0flows])
>> >
>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.10 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
>> action=(ct_snat_in_czone;)
>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.10 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
>> flags.use_snat_zone == 1), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.20 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
>> action=(ct_snat_in_czone;)
>> > @@ -5876,6 +5904,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.10), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.100), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.200), action=(ct_dnat;)
>> > @@ -5884,6 +5913,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.20 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.0.200 &&
>> is_chassis_resident("cr-lr0-public")),
>> action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 &&
>> is_chassis_resident("cr-lr0-public")),
>> action=(ct_lb_mark(backends=10.0.0.4:8080);)
>> > @@ -5916,10 +5946,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> >    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src
>> == 10.0.0.0/24 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>> action=(ct_snat_in_czone(172.168.0.10);)
>> >    table=??(lr_out_snat        ), priority=154  , match=(ip && ip4.src
>> == 10.0.0.0/24 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl) && reg9[[4]]
>> == 1), action=(reg9[[4]] = 0; ct_snat(172.168.0.10);)
>> > @@ -5939,6 +5971,7 @@ AT_CAPTURE_FILE([lr0flows])
>> >
>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.10 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.20 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.30 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>> > @@ -5946,6 +5979,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.10), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.100), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.200), action=(ct_dnat;)
>> > @@ -5954,6 +5988,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.20 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);)
>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.0.200 &&
>> is_chassis_resident("cr-lr0-public")),
>> action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 &&
>> is_chassis_resident("cr-lr0-public")),
>> action=(ct_lb_mark(backends=10.0.0.4:8080);)
>> > @@ -5982,24 +6017,20 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src
>> == 10.0.0.0/24 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>> action=(ct_next(snat);)
>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src
>> == 10.0.0.10 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>> action=(ct_next(snat);)
>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> > -  table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.dst
>> == 10.0.0.0/24 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>> >    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src
>> == 10.0.0.0/24 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>> action=(ct_snat(172.168.0.10);)
>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>> == 10.0.0.10 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>> == 10.0.0.10 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>> action=(ct_snat(172.168.0.30);)
>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>> == 10.0.0.3 && outport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>> action=(ct_snat(172.168.0.20);)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_snat   ), priority=153  , match=(ip && ip4.dst
>> == 10.0.0.0/24 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && ct.new),
>> action=(ct_commit_to_zone(snat);)
>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>> == 10.0.0.10 && inport == "lr0-public" &&
>> is_chassis_resident("cr-lr0-public") && ct.new),
>> action=(ct_commit_to_zone(snat);)
>> >  ])
>> >
>> >  # Make the logical router as Gateway router
>> > @@ -6013,6 +6044,7 @@ AT_CAPTURE_FILE([lr0flows])
>> >
>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>> == 172.168.0.10), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>> == 172.168.0.20), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>> == 172.168.0.30), action=(ct_snat;)
>> > @@ -6020,6 +6052,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.10), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.100), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.200), action=(ct_dnat;)
>> > @@ -6028,6 +6061,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.0.200),
>> action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>> action=(ct_lb_mark(backends=10.0.0.4:8080);)
>> > @@ -6053,11 +6087,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
>> action=(ct_commit { } ; next; )
>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> >    table=??(lr_out_snat        ), priority=25   , match=(ip && ip4.src
>> == 10.0.0.0/24 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);)
>> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
>> == 10.0.0.10 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.30);)
>> > @@ -6074,6 +6109,7 @@ AT_CAPTURE_FILE([lr0flows])
>> >
>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>> == 172.168.0.10), action=(ct_snat;)
>> > @@ -6083,6 +6119,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.10), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.100), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.200), action=(ct_dnat;)
>> > @@ -6091,6 +6128,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.0.200),
>> action=(flags.force_snat_for_lb = 1;
>> ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
>> force_snat);)
>> > @@ -6116,11 +6154,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
>> action=(ct_commit { } ; next; )
>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=110  ,
>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
>> action=(ct_snat(172.168.0.10);)
>> >    table=??(lr_out_snat        ), priority=110  ,
>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
>> action=(ct_snat(10.0.0.1);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> > @@ -6138,6 +6177,7 @@ AT_CAPTURE_FILE([lr0flows])
>> >
>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>> == 172.168.0.10), action=(ct_snat;)
>> > @@ -6147,6 +6187,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.10), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.10), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.100), action=(ct_dnat;)
>> > @@ -6156,6 +6197,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.0.200),
>> action=(flags.force_snat_for_lb = 1;
>> ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
>> force_snat);)
>> > @@ -6182,11 +6224,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
>> action=(ct_commit { } ; next; )
>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=110  ,
>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
>> action=(ct_snat(172.168.0.10);)
>> >    table=??(lr_out_snat        ), priority=110  ,
>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
>> action=(ct_snat(10.0.0.1);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> > @@ -6212,6 +6255,7 @@ AT_CAPTURE_FILE([lr0flows])
>> >
>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-public" && ip6.dst == def0::10), action=(ct_snat;)
>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>> > @@ -6223,6 +6267,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 10.0.0.10), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.10), action=(ct_dnat;)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.100), action=(ct_dnat;)
>> > @@ -6233,6 +6278,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.0.200),
>> action=(flags.force_snat_for_lb = 1;
>> ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
>> force_snat);)
>> > @@ -6260,11 +6306,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
>> action=(ct_commit { } ; next; )
>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=110  ,
>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
>> action=(ct_snat(172.168.0.10);)
>> >    table=??(lr_out_snat        ), priority=110  ,
>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
>> action=(ct_snat(10.0.0.1);)
>> >    table=??(lr_out_snat        ), priority=110  ,
>> match=(flags.force_snat_for_lb == 1 && ip6 && outport == "lr0-public"),
>> action=(ct_snat(def0::10);)
>> > @@ -6291,15 +6338,18 @@ AT_CAPTURE_FILE([lr0flows])
>> >
>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>> action=(flags.without_unsnat = 1; next;)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl)), action=(ct_next(dnat);)
>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>> == 172.168.0.210), action=(ct_dnat;)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.0.210 && tcp && tcp.dst == 60),
>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.50:6062,
>> 10.0.0.60:6062; force_snat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.0.210 && udp && udp.dst == 60),
>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.50:6062,
>> 10.0.0.60:6062; force_snat);)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>> > @@ -6322,11 +6372,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>> ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip && ct.new),
>> action=(ct_commit { } ; next; )
>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip && (!ct.trk
>> || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>> >  ])
>> >
>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>> action=(next;)
>> >  ])
>> >
>> > @@ -6358,6 +6409,7 @@ check ovn-nbctl --wait=sb sync
>> >
>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
>> ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.10.10), action=(reg0 = 0; reject {
>> outport <-> inport; next(pipeline=egress,table=??);};)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
>> !ct.est && !ct.new), action=(ct_commit_nat;)
>> > @@ -6372,6 +6424,7 @@ check ovn-nbctl --wait=sb set load_balancer lb5
>> options:skip_snat=true
>> >
>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
>> ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.10.10), action=(flags.skip_snat_for_lb
>> = 1; reg0 = 0; reject { outport <-> inport;
>> next(pipeline=egress,table=??);};)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
>> !ct.est && !ct.new), action=(ct_commit_nat;)
>> > @@ -6388,6 +6441,7 @@ check ovn-nbctl --wait=sb set logical_router lr0
>> options:lb_force_snat_ip="route
>> >
>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
>> ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.10.10),
>> action=(flags.force_snat_for_lb = 1; reg0 = 0; reject { outport <-> inport;
>> next(pipeline=egress,table=??);};)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
>> !ct.est && !ct.new), action=(ct_commit_nat;)
>> > @@ -6405,6 +6459,7 @@ check ovn-nbctl --wait=sb lr-lb-add lr0 lb6
>> >
>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
>> ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.10.30), action=(drop;)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
>> !ct.est && !ct.new), action=(ct_commit_nat;)
>> > @@ -6419,6 +6474,7 @@ check ovn-nbctl --wait=sb set load_balancer lb6
>> options:skip_snat=true
>> >
>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
>> ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.10.30), action=(flags.skip_snat_for_lb
>> = 1; drop;)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
>> !ct.est && !ct.new), action=(ct_commit_nat;)
>> > @@ -6435,6 +6491,7 @@ check ovn-nbctl --wait=sb set logical_router lr0
>> options:lb_force_snat_ip="route
>> >
>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
>> ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.168.10.30),
>> action=(flags.force_snat_for_lb = 1; drop;)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
>> !ct.est && !ct.new), action=(ct_commit_nat;)
>> > @@ -7951,9 +8008,6 @@ AT_CHECK([grep lr_in_unsnat lrflows | grep
>> ct_snat | ovn_strip_lflows], [0], [dn
>> >  ])
>> >
>> >  AT_CHECK([grep lr_out_snat lrflows | grep ct_snat | ovn_strip_lflows],
>> [0], [dnl
>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>> == 20.0.0.10 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1")),
>> action=(ct_snat;)
>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>> == 20.0.0.10 && inport == "DR-S2" && is_chassis_resident("cr-DR-S2")),
>> action=(ct_snat;)
>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>> == 20.0.0.10 && inport == "DR-S3" && is_chassis_resident("cr-DR-S3")),
>> action=(ct_snat;)
>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>> == 20.0.0.10 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>> (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.10);)
>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>> == 20.0.0.10 && outport == "DR-S2" && is_chassis_resident("cr-DR-S2") &&
>> (!ct.trk || !ct.rpl)), action=(ct_snat(10.0.0.10);)
>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>> == 20.0.0.10 && outport == "DR-S3" && is_chassis_resident("cr-DR-S3") &&
>> (!ct.trk || !ct.rpl)), action=(ct_snat(192.168.0.10);)
>> > @@ -7961,9 +8015,6 @@ AT_CHECK([grep lr_out_snat lrflows | grep ct_snat
>> | ovn_strip_lflows], [0], [dnl
>> >
>> >  AT_CHECK([grep lr_out_post_snat lrflows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
>> action=(next;)
>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>> == 20.0.0.10 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>> ct.new), action=(ct_commit_to_zone(snat);)
>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>> == 20.0.0.10 && inport == "DR-S2" && is_chassis_resident("cr-DR-S2") &&
>> ct.new), action=(ct_commit_to_zone(snat);)
>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>> == 20.0.0.10 && inport == "DR-S3" && is_chassis_resident("cr-DR-S3") &&
>> ct.new), action=(ct_commit_to_zone(snat);)
>> >  ])
>> >
>> >  check ovn-nbctl --wait=sb lr-nat-del DR snat 20.0.0.10
>> > @@ -9387,6 +9438,7 @@ AT_CHECK([grep "lr_in_lb_aff_check" R1flows |
>> ovn_strip_lflows], [0], [dnl
>> >  ])
>> >  AT_CHECK([grep "lr_in_dnat " R1flows | ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
>> action=(ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);)
>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
>> && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
>> && ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 && reg8[[0..15]] ==
>> 80), action=(reg0 = 172.16.0.10; ct_lb_mark(backends=20.0.0.2:80);)
>> > @@ -9411,6 +9463,7 @@ AT_CAPTURE_FILE([R1flows_skip_snat])
>> >
>> >  AT_CHECK([grep "lr_in_dnat " R1flows_skip_snat | ovn_strip_lflows],
>> [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
>> action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
>> 20.0.0.2:80; skip_snat);)
>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
>> && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
>> && ct.new && ip4.dst == 172.16.0.10 && 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);)
>> > @@ -9432,6 +9485,7 @@ AT_CAPTURE_FILE([R1flows_force_snat])
>> >
>> >  AT_CHECK([grep "lr_in_dnat " R1flows_force_snat | ovn_strip_lflows],
>> [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
>> 20.0.0.2:80; force_snat);)
>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
>> && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
>> && ct.new && ip4.dst == 172.16.0.10 && 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);)
>> > @@ -9452,6 +9506,7 @@ AT_CAPTURE_FILE([R1flows_force_skip_snat])
>> >
>> >  AT_CHECK([grep "lr_in_dnat " R1flows_force_skip_snat |
>> ovn_strip_lflows], [0], [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
>> action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
>> 20.0.0.2:80; skip_snat);)
>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
>> && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
>> && ct.new && ip4.dst == 172.16.0.10 && 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);)
>> > @@ -9476,6 +9531,7 @@ AT_CAPTURE_FILE([R1flows_2lbs])
>> >
>> >  AT_CHECK([grep "lr_in_dnat " R1flows_2lbs | ovn_strip_lflows], [0],
>> [dnl
>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>> action=(next;)
>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip && ct.new),
>> action=(ct_commit_to_zone(dnat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
>> action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
>> 20.0.0.2:80; skip_snat);)
>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>> !ct.rel && ip4 && ip4.dst == 172.16.0.20 && tcp && tcp.dst == 80),
>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
>> 20.0.0.2:80; force_snat);)
>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] == 1
>> && ct.new && ip4.dst == 172.16.0.10 && 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);)
>> > diff --git a/tests/system-ovn.at b/tests/system-ovn.at
>> > index 861b1cb99..dc7b0ab2e 100644
>> > --- a/tests/system-ovn.at
>> > +++ b/tests/system-ovn.at
>> > @@ -117,6 +117,7 @@ NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2
>> 30.0.0.2 | FORMAT_PING], \
>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.2) | \
>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>> >
>>  icmp,orig=(src=172.16.1.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>> +icmp,orig=(src=172.16.1.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>>  icmp,orig=(src=172.16.1.2,dst=30.0.0.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>> >  ])
>> >
>> > @@ -297,6 +298,7 @@ NS_CHECK_EXEC([alice1], [ping6 -q -c 3 -i 0.3 -w 2
>> fd30::2 | FORMAT_PING], \
>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd21::2) | \
>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>> >
>>  icmpv6,orig=(src=fd21::2,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>> >
>> +icmpv6,orig=(src=fd21::2,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>> >
>>  icmpv6,orig=(src=fd21::2,dst=fd30::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>> >  ])
>> >
>> > @@ -3753,6 +3755,7 @@ NS_CHECK_EXEC([foo2], [ping6 -q -c 3 -i 0.3 -w 2
>> fd20::2 | FORMAT_PING], \
>> >  ovs-appctl dpctl/dump-conntrack | grep icmpv6
>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd11::3) | \
>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>> >
>> +icmpv6,orig=(src=fd11::3,dst=fd20::2,id=<cleared>,type=128,code=0),reply=(src=fd20::2,dst=fd11::3,id=<cleared>,type=129,code=0),zone=<cleared>
>> >  ])
>> >
>> >  # We verify that SNAT indeed happened via 'dump-conntrack' command.
>> > @@ -3938,6 +3941,8 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2
>> 192.168.2.2 | FORMAT_PING], \
>> >  # We verify that the connection is not tracked.
>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
>> FORMAT_CT(192.168.2.2) | \
>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>> >
>> +icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>> +icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>> >  ])
>> >
>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>> > @@ -3950,6 +3955,8 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
>> 192.168.2.2 | FORMAT_PING], \
>> >  # We verify that the connection is not tracked.
>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
>> FORMAT_CT(192.168.2.2) | \
>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>> >
>> +icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>> +icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>> >  ])
>> >
>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>> > @@ -3959,9 +3966,11 @@ NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2
>> 192.168.1.3 | FORMAT_PING], \
>> >  3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> >  ])
>> >
>> > -# We verify that the connection is not tracked.
>> > +# We verify that the connection is tracked.
>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
>> FORMAT_CT(192.168.2.2) | \
>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>> >
>> +icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>> +icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>> >  ])
>> >
>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>> > @@ -3978,6 +3987,7 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep
>> icmp | FORMAT_CT(172.16.1.4) |
>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>> >
>>  icmp,orig=(src=172.16.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>>  icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>> +icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>> >  ])
>> >
>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>> > @@ -3993,7 +4003,6 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
>> 172.16.1.4 | FORMAT_PING], \
>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
>> FORMAT_CT(172.16.1.1) | \
>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>> >
>>  icmp,orig=(src=172.16.1.1,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>> -icmp,orig=(src=172.16.1.1,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>>  icmp,orig=(src=192.168.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>> >  ])
>> >
>> > @@ -4144,6 +4153,7 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2
>> fd20::4 | FORMAT_PING], \
>> >  # Then DNAT of 'bar1' address happens (listed first below).
>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::4) | \
>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>> >
>> +icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd11::2,id=<cleared>,type=129,code=0),zone=<cleared>
>> >
>>  icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
>> >
>>  icmpv6,orig=(src=fd20::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
>> >  ])
>> > @@ -4161,7 +4171,6 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
>> fd20::4 | FORMAT_PING], \
>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \
>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>> >
>>  icmpv6,orig=(src=fd11::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>> >
>> -icmpv6,orig=(src=fd20::1,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>> >
>>  icmpv6,orig=(src=fd20::1,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>> >  ])
>> >
>> > @@ -8682,10 +8691,10 @@ test_ping sw11 192.168.1.2
>> >  OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep -v "n_packets=0" |
>> grep 'nat(src=172.16.1.21)'])
>> >  # Ensure conntrack entry is present
>> >  OVS_WAIT_FOR_OUTPUT([
>> > -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
>> > +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>> >        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>> >
>> -icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>> -tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>> >
>> +icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>> +tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>> >  ])
>> >
>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>> > @@ -8697,9 +8706,11 @@ test_ping sw11 192.168.1.2
>> >
>> >  # Ensure conntrack entry is present
>> >  OVS_WAIT_FOR_OUTPUT([
>> > -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
>> > +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>> >        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>> >
>> +icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>>  icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>> +tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>> >
>>  tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>> >  ])
>> >
>> > @@ -8711,10 +8722,10 @@ test_ping sw11 172.16.1.2
>> >
>> >  # Ensure conntrack entry is present
>> >  OVS_WAIT_FOR_OUTPUT([
>> > -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
>> > +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>> >        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>> >
>> -icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>> -tcp,orig=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>> >
>> +icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>> >
>> +tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>> >  ])
>> >
>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>> > --
>> > 2.46.2
>> >
>> > _______________________________________________
>> > dev mailing list
>> > dev@openvswitch.org
>> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>
>
>
> --
>
> Ales Musil
>
> Senior Software Engineer - OVN Core
>
> Red Hat EMEA <https://www.redhat.com>
>
> amusil@redhat.com
> <https://red.ht/sig>
>
Ales Musil Nov. 14, 2024, 7:58 a.m. UTC | #5
On Thu, Nov 14, 2024 at 8:35 AM Han Zhou <hzhou@ovn.org> wrote:

>
>
> On Wed, Nov 13, 2024 at 2:00 AM Ales Musil <amusil@redhat.com> wrote:
>
>>
>>
>> On Wed, Nov 13, 2024 at 10:37 AM Han Zhou <hzhou@ovn.org> wrote:
>>
>>> Thanks Ales.
>>>
>>>
>> Hi Han,
>>
>>
>>>
>>> On Tue, Oct 8, 2024 at 3:12 AM Ales Musil <amusil@redhat.com> wrote:
>>> >
>>> > Commit all traffic that is not already commit by either NAT or LB. This
>>> > ensures that the traffic is tracked, and we don't erroneously commit
>>> > reply traffic, or reply traffic is not marked as invalid.
>>> >
>>> > To achieve the commit we need to perform lookup on every packet
>>> > that goes through LR pipeline whenever there is stateful NAT.
>>> >
>>> > The SNAT lookup requires additional flag as the unSNAT is happening
>>> > in ingress pipeline and at that point we need to know if the packet
>>> > is reply or not. This is not required for DNAT, because unDNAT stage
>>> > happens in egress.
>>>
>>> Could you help explain in a little more detail how the ct.rpl relates to
>>> this new without_unsnat flag? I still can't figure out why this flag is
>>> needed.
>>>
>>
>> Without it we would commit reply traffic for SNAT which is wrong. That
>> caused all sorts of issues.
>>
>>
> I understand that we shouldn't commit reply traffic, but my question was
> why does the "without_unsnat" flag avoid that. I don't see any link between
> the without_unsnat and ct.rpl. It may be helpful to add comments in the
> code as well.
>

Sure I can add a comment to make it more obvious.


>
>
>>
>>> In addition, it seems that with this flag, you commit to snat zone only
>>> if the packet's without_unsnat == 1, but even if the packet did perform
>>> unSNAT we would still want to commit it to SNAT zone, because of the HW
>>> offload issue reported here:
>>> https://mail.openvswitch.org/pipermail/ovs-dev/2024-August/417025.html
>>>
>>
>> I'm probably missing something, why would we want to commit traffic that
>> did unSNAT? That traffic is already committed in the SNAT zone. The issue
>> described in the thread should be gone, AFAIU the problem was that the
>> traffic wasn't committed in the original direction, then unSNAT would mark
>> it as new. That shouldn't happen with this series.
>>
>>
> My understanding was, the lflow you added with priority 10 sets the
> without_unsnat flag for the packets that didn't perform ct_snat at the
> UNSNAT stage, and later you commit these to the SNAT zone. However, for the
> other packets that performed the ct_snat at the UNSNAT stage, having
> traversed the SNAT zone doesn't necessarily mean they were committed to the
> zone, right? So with your change, if a packet was sent to the CT at the
> UNSNAT stage, it would never get committed to SNAT zone, if not committed
> at any other stages. Did I miss anything?
>

The unSNAT rules should correspond to regular SNAT, in other words if we
have unSNAT for a certain match the same traffic pattern should match on
SNAT in the original direction. Unless I'm missing some case where the
original traffic goes through unSNAT but not SNAT it should be covered.

Thinking about this there is one scenario that might be problematic and
that is a combination of stateful and stateless SNAT, the stateless SNAT
traffic wouldn't be committed. Is that what you had in mind or is it a
different scenario? In any case I can fix that with proper iteration by
setting this flag for stateless SNATs.

Thanks,
Ales


>
> Thanks,
> Han
>
>
>>
>>> BTW, I have been trying my best but I feel it is quite difficult to
>>> reason about the correctness of the DNAT/SNAT pipelines, which have become
>>> so complex. I hope our test case coverage is sufficient for all kinds of
>>> corner cases :)
>>>
>>
>> Yeah I understand and we probably have decent coverage because some of
>> my  thoughts were quickly disputed by failing and misbehaving tests :)
>>
>>>
>>> I am still working on performance test for this patch. Sorry it took so
>>> long but I still need some more time.
>>>
>>>
>> This is one of the things that we shouldn't rush, thank you for looking
>> into the performance testing!
>>
>>
>>>
>>> Regards,
>>> Han
>>>
>>>
>> Regards,
>> Ales
>>
>>
>>> >
>>> > Signed-off-by: Ales Musil <amusil@redhat.com>
>>> > ---
>>> > There is one failing system test with userspace datapath, that's due
>>> > to the recirculation limit that is being hit due to additional
>>> > lookups.
>>> > ---
>>> >  include/ovn/logical-fields.h |   4 ++
>>> >  lib/logical-fields.c         |   4 ++
>>> >  northd/northd.c              |  76 ++++++++++++----------
>>> >  tests/ovn-northd.at          | 118
>>> ++++++++++++++++++++++++++---------
>>> >  tests/system-ovn.at          |  31 ++++++---
>>> >  5 files changed, 158 insertions(+), 75 deletions(-)
>>> >
>>> > diff --git a/include/ovn/logical-fields.h
>>> b/include/ovn/logical-fields.h
>>> > index d6c4a9b6b..cc1f50ff2 100644
>>> > --- a/include/ovn/logical-fields.h
>>> > +++ b/include/ovn/logical-fields.h
>>> > @@ -82,6 +82,7 @@ enum mff_log_flags_bits {
>>> >      MLF_LOCALNET_BIT = 15,
>>> >      MLF_RX_FROM_TUNNEL_BIT = 16,
>>> >      MLF_ICMP_SNAT_BIT = 17,
>>> > +    MLF_WITHOUT_UNSNAT_BIT = 18,
>>> >  };
>>> >
>>> >  /* MFF_LOG_FLAGS_REG flag assignments */
>>> > @@ -137,6 +138,9 @@ enum mff_log_flags {
>>> >      MLF_RX_FROM_TUNNEL = (1 << MLF_RX_FROM_TUNNEL_BIT),
>>> >
>>> >      MLF_ICMP_SNAT = (1 << MLF_ICMP_SNAT_BIT),
>>> > +
>>> > +    /* Indicate that the packet didn't go through unSNAT. */
>>> > +    MLF_WITHOUT_UNSNAT = (1 << MLF_WITHOUT_UNSNAT_BIT),
>>> >  };
>>> >
>>> >  /* OVN logical fields
>>> > diff --git a/lib/logical-fields.c b/lib/logical-fields.c
>>> > index 5a8b53f2b..c63e19897 100644
>>> > --- a/lib/logical-fields.c
>>> > +++ b/lib/logical-fields.c
>>> > @@ -139,6 +139,10 @@ ovn_init_symtab(struct shash *symtab)
>>> >                               flags_str);
>>> >      snprintf(flags_str, sizeof flags_str, "flags[%d]",
>>> MLF_RX_FROM_TUNNEL_BIT);
>>> >      expr_symtab_add_subfield(symtab, "flags.tunnel_rx", NULL,
>>> flags_str);
>>> > +    snprintf(flags_str, sizeof flags_str, "flags[%d]",
>>> > +             MLF_WITHOUT_UNSNAT_BIT);
>>> > +    expr_symtab_add_subfield(symtab, "flags.without_unsnat", NULL,
>>> > +                             flags_str);
>>> >
>>> >      /* Connection tracking state. */
>>> >      expr_symtab_add_field_scoped(symtab, "ct_mark", MFF_CT_MARK,
>>> NULL, false,
>>> > diff --git a/northd/northd.c b/northd/northd.c
>>> > index 0364dd766..a42057e45 100644
>>> > --- a/northd/northd.c
>>> > +++ b/northd/northd.c
>>> > @@ -15987,8 +15987,7 @@ build_lrouter_out_snat_flow(struct lflow_table
>>> *lflows,
>>> >                              struct ds *actions, bool distributed_nat,
>>> >                              struct eth_addr mac, int cidr_bits, bool
>>> is_v6,
>>> >                              struct ovn_port *l3dgw_port,
>>> > -                            struct lflow_ref *lflow_ref,
>>> > -                            const struct chassis_features *features)
>>> > +                            struct lflow_ref *lflow_ref)
>>> >  {
>>> >      if (!(nat_entry->type == SNAT || nat_entry->type ==
>>> DNAT_AND_SNAT)) {
>>> >          return;
>>> > @@ -16019,34 +16018,6 @@ build_lrouter_out_snat_flow(struct
>>> lflow_table *lflows,
>>> >                              priority, ds_cstr(match),
>>> >                              ds_cstr(actions), &nat->header_,
>>> >                              lflow_ref);
>>> > -
>>> > -    /* For the SNAT networks, we need to make sure that connections
>>> are
>>> > -     * properly tracked so we can decide whether to perform SNAT on
>>> traffic
>>> > -     * exiting the network. */
>>> > -    if (features->ct_commit_to_zone && features->ct_next_zone &&
>>> > -        nat_entry->type == SNAT && !od->is_gw_router) {
>>> > -        /* For traffic that comes from SNAT network, initiate CT
>>> state before
>>> > -         * entering S_ROUTER_OUT_SNAT to allow matching on various CT
>>> states.
>>> > -         */
>>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 70,
>>> > -                      ds_cstr(match), "ct_next(snat);",
>>> > -                      lflow_ref);
>>> > -
>>> > -        build_lrouter_out_snat_match(lflows, od, nat, match,
>>> > -                                     distributed_nat, cidr_bits,
>>> is_v6,
>>> > -                                     l3dgw_port, lflow_ref, true);
>>> > -
>>> > -        /* New traffic that goes into SNAT network is committed to CT
>>> to avoid
>>> > -         * SNAT-ing replies.*/
>>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, priority,
>>> > -                      ds_cstr(match), "ct_snat;",
>>> > -                      lflow_ref);
>>> > -
>>> > -        ds_put_cstr(match, " && ct.new");
>>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_SNAT, priority,
>>> > -                      ds_cstr(match), "ct_commit_to_zone(snat);",
>>> > -                      lflow_ref);
>>> > -    }
>>> >  }
>>> >
>>> >  static void
>>> > @@ -16439,9 +16410,6 @@ build_lrouter_nat_defrag_and_lb(
>>> >          ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 50,
>>> >                        "ip", "flags.loopback = 1; ct_dnat;",
>>> >                        lflow_ref);
>>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 50,
>>> > -                      "ip && ct.new", "ct_commit { } ; next; ",
>>> > -                      lflow_ref);
>>> >      }
>>> >
>>> >      /* NAT rules are only valid on Gateway routers and routers with
>>> > @@ -16459,6 +16427,9 @@ build_lrouter_nat_defrag_and_lb(
>>> >          !lport_addresses_is_empty(&lrnat_rec->dnat_force_snat_addrs);
>>> >      bool lb_force_snat_ip =
>>> >          !lport_addresses_is_empty(&lrnat_rec->lb_force_snat_addrs);
>>> > +    bool stateful_dnat = lr_stateful_rec->has_lb_vip;
>>> > +    bool stateful_snat = (dnat_force_snat_ip || lb_force_snat_ip ||
>>> > +                          lrnat_rec->lb_force_snat_router_ip);
>>> >
>>> >      for (size_t i = 0; i < lrnat_rec->n_nat_entries; i++) {
>>> >          struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i];
>>> > @@ -16477,6 +16448,21 @@ build_lrouter_nat_defrag_and_lb(
>>> >              continue;
>>> >          }
>>> >
>>> > +        if (!stateless) {
>>> > +            switch (nat_entry->type) {
>>> > +            case DNAT:
>>> > +                stateful_dnat = true;
>>> > +                break;
>>> > +            case SNAT:
>>> > +                stateful_snat = true;
>>> > +                break;
>>> > +            case DNAT_AND_SNAT:
>>> > +                stateful_snat = true;
>>> > +                stateful_dnat = true;
>>> > +                break;
>>> > +            }
>>> > +        }
>>> > +
>>> >          /* S_ROUTER_IN_UNSNAT
>>> >           * Ingress UNSNAT table: It is for already established
>>> connections'
>>> >           * reverse traffic. i.e., SNAT has already been done in egress
>>> > @@ -16599,7 +16585,7 @@ build_lrouter_nat_defrag_and_lb(
>>> >          } else {
>>> >              build_lrouter_out_snat_flow(lflows, od, nat_entry, match,
>>> actions,
>>> >                                          distributed_nat, mac,
>>> cidr_bits, is_v6,
>>> > -                                        l3dgw_port, lflow_ref,
>>> features);
>>> > +                                        l3dgw_port, lflow_ref);
>>> >          }
>>> >
>>> >          /* S_ROUTER_IN_ADMISSION - S_ROUTER_IN_IP_INPUT */
>>> > @@ -16689,6 +16675,28 @@ build_lrouter_nat_defrag_and_lb(
>>> >          }
>>> >      }
>>> >
>>> > +
>>> > +    bool can_commit = features->ct_commit_to_zone &&
>>> features->ct_next_zone;
>>> > +    if (can_commit && stateful_dnat) {
>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 10,
>>> > +                      "ip && (!ct.trk || !ct.rpl)",
>>> > +                      "ct_next(dnat);", lflow_ref);
>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 10,
>>> > +                      "ip && ct.new", "ct_commit_to_zone(dnat);",
>>> lflow_ref);
>>> > +    }
>>> > +
>>> > +    if (can_commit && stateful_snat) {
>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 10,
>>> > +                      "ip", "flags.without_unsnat = 1; next;",
>>> lflow_ref);
>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 10,
>>> > +                      "ip && (!ct.trk || !ct.rpl) && "
>>> > +                      "flags.without_unsnat == 1", "ct_next(snat);",
>>> > +                      lflow_ref);
>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 10,
>>> > +                      "ip && ct.new && flags.without_unsnat == 1",
>>> > +                      "ct_commit_to_zone(snat);", lflow_ref);
>>> > +    }
>>> > +
>>> >      if (use_common_zone && od->nbr->n_nat) {
>>> >          ds_clear(match);
>>> >          ds_put_cstr(match, "ip && ct_mark.natted == 1");
>>> > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>>> > index d6a8c4640..96e28a54a 100644
>>> > --- a/tests/ovn-northd.at
>>> > +++ b/tests/ovn-northd.at
>>> > @@ -1181,18 +1181,18 @@ AT_CAPTURE_FILE([crflows])
>>> >
>>> >  AT_CHECK([grep -e "lr_out_snat" drflows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>>> == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>>> ip4.src == $allowed_range), action=(ct_snat;)
>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>>> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>>> ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
>>> action=(ct_snat(172.16.1.1);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep -e "lr_out_post_snat" drflows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>>> == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>>> ip4.src == $allowed_range && ct.new), action=(ct_commit_to_zone(snat);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep -e "lr_out_snat" crflows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
>>> == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
>>> action=(ct_snat(172.16.1.1);)
>>> >  ])
>>> > @@ -1220,19 +1220,19 @@ AT_CAPTURE_FILE([crflows2])
>>> >
>>> >  AT_CHECK([grep -e "lr_out_snat" drflows2 | ovn_strip_lflows], [0],
>>> [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>>> == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1")),
>>> action=(ct_snat;)
>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>>> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>>> (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
>>> >    table=??(lr_out_snat        ), priority=163  , match=(ip && ip4.src
>>> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>>> ip4.dst == $disallowed_range), action=(next;)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep -e "lr_out_post_snat" drflows2 | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>>> == 50.0.0.11 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>>> ct.new), action=(ct_commit_to_zone(snat);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep -e "lr_out_snat" crflows2 | ovn_strip_lflows], [0],
>>> [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
>>> == 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
>>> >    table=??(lr_out_snat        ), priority=35   , match=(ip && ip4.src
>>> == 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
>>> > @@ -1259,6 +1259,7 @@ AT_CAPTURE_FILE([crflows2])
>>> >
>>> >  AT_CHECK([grep -e "lr_out_snat" drflows3 | ovn_strip_lflows], [0],
>>> [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>>> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>>> ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
>>> action=(ct_snat(172.16.1.2);)
>>> >  ])
>>> > @@ -1269,6 +1270,7 @@ AT_CHECK([grep -e "lr_out_post_snat" drflows3 |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep -e "lr_out_snat" crflows3 | ovn_strip_lflows], [0],
>>> [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
>>> == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
>>> action=(ct_snat(172.16.1.2);)
>>> >  ])
>>> > @@ -1294,6 +1296,7 @@ AT_CAPTURE_FILE([crflows2])
>>> >
>>> >  AT_CHECK([grep -e "lr_out_snat" drflows4 | ovn_strip_lflows], [0],
>>> [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>>> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>>> (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
>>> >    table=??(lr_out_snat        ), priority=163  , match=(ip && ip4.src
>>> == 50.0.0.11 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>>> ip4.dst == $disallowed_range), action=(next;)
>>> > @@ -1301,6 +1304,7 @@ AT_CHECK([grep -e "lr_out_snat" drflows4 |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep -e "lr_out_snat" crflows4 | ovn_strip_lflows], [0],
>>> [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
>>> == 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
>>> >    table=??(lr_out_snat        ), priority=35   , match=(ip && ip4.src
>>> == 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
>>> > @@ -1663,6 +1667,7 @@ AT_CAPTURE_FILE([sbflows])
>>> >  # dnat_and_snat or snat entry.
>>> >  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>>> == 192.168.2.1), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>>> == 192.168.2.4), action=(ct_snat;)
>>> >  ])
>>> > @@ -1693,6 +1698,7 @@ AT_CAPTURE_FILE([sbflows])
>>> >  # dnat_and_snat or snat entry.
>>> >  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>>> == 192.168.2.1), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>>> == 192.168.2.4), action=(ct_snat;)
>>> >  ])
>>> > @@ -1801,6 +1807,7 @@ ovn-nbctl --wait=sb sync
>>> >
>>> >  AT_CHECK([ovn-sbctl lflow-list lr0 | grep lr_in_unsnat |
>>> ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 &&
>>> ip4.dst == 192.168.2.3), action=(ct_snat;)
>>> >  ])
>>> >
>>> > @@ -4277,12 +4284,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.10), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.100), action=(ct_dnat;)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>>> action=(ct_lb_mark(backends=10.0.0.4:8080);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
>>> action=(ct_lb_mark(backends=10.0.0.40:8080);)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>> > @@ -4302,18 +4311,21 @@ AT_CAPTURE_FILE([lr0flows])
>>> >
>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 &&
>>> ip4.dst == 20.0.0.4), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip6 &&
>>> ip6.dst == aef0::4), action=(ct_snat;)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.10), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.100), action=(ct_dnat;)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
>>> force_snat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
>>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
>>> force_snat);)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>> > @@ -4326,6 +4338,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=100  ,
>>> match=(flags.force_snat_for_lb == 1 && ip4), action=(ct_snat(20.0.0.4);)
>>> >    table=??(lr_out_snat        ), priority=100  ,
>>> match=(flags.force_snat_for_lb == 1 && ip6), action=(ct_snat(aef0::4);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> > @@ -4339,7 +4352,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
>>> ct.new), action=(ct_commit { } ; next; )
>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>> >  ])
>>> >
>>> >  check ovn-nbctl --wait=sb set logical_router lr0
>>> options:lb_force_snat_ip="router_ip"
>>> > @@ -4352,6 +4365,7 @@ AT_CHECK([grep "lr_in_ip_input" lr0flows | grep
>>> "priority=60" | sort], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
>>> > @@ -4359,12 +4373,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.10), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.100), action=(ct_dnat;)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
>>> force_snat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
>>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
>>> force_snat);)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>> > @@ -4377,6 +4393,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=110  ,
>>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
>>> action=(ct_snat(172.168.0.100);)
>>> >    table=??(lr_out_snat        ), priority=110  ,
>>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
>>> action=(ct_snat(10.0.0.1);)
>>> >    table=??(lr_out_snat        ), priority=110  ,
>>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
>>> action=(ct_snat(20.0.0.1);)
>>> > @@ -4391,7 +4408,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
>>> ct.new), action=(ct_commit { } ; next; )
>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>> >  ])
>>> >
>>> >  check ovn-nbctl --wait=sb remove logical_router lr0 options chassis
>>> > @@ -4416,6 +4433,7 @@ AT_CAPTURE_FILE([lr0flows])
>>> >
>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
>>> > @@ -4424,12 +4442,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.10), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.100), action=(ct_dnat;)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
>>> force_snat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
>>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
>>> force_snat);)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>> > @@ -4442,6 +4462,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=110  ,
>>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
>>> action=(ct_snat(172.168.0.100);)
>>> >    table=??(lr_out_snat        ), priority=110  ,
>>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
>>> action=(ct_snat(10.0.0.1);)
>>> >    table=??(lr_out_snat        ), priority=110  ,
>>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
>>> action=(ct_snat(20.0.0.1);)
>>> > @@ -4457,7 +4478,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
>>> ct.new), action=(ct_commit { } ; next; )
>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>> >  ])
>>> >
>>> >  check ovn-nbctl --wait=sb lb-add lb2 10.0.0.20:80 10.0.0.40:8080
>>> > @@ -4468,6 +4489,7 @@ ovn-sbctl dump-flows lr0 > lr0flows
>>> >
>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
>>> > @@ -4476,6 +4498,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.100), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.20), action=(ct_dnat;)
>>> >  ])
>>> > @@ -4498,7 +4521,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
>>> ct.new), action=(ct_commit { } ; next; )
>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>> >  ])
>>> >
>>> >  AT_CLEANUP
>>> > @@ -5744,6 +5767,7 @@ AT_CAPTURE_FILE([lr0flows])
>>> >
>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.10 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
>>> action=(ct_snat_in_czone;)
>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.10 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
>>> flags.use_snat_zone == 1), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.20 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
>>> action=(ct_snat_in_czone;)
>>> > @@ -5754,10 +5778,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.20 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
>>> >  ])
>>> >
>>> > @@ -5776,10 +5802,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> >    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src
>>> == 10.0.0.0/24 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>>> action=(ct_snat_in_czone(172.168.0.10);)
>>> >    table=??(lr_out_snat        ), priority=154  , match=(ip && ip4.src
>>> == 10.0.0.0/24 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl) && reg9[[4]]
>>> == 1), action=(reg9[[4]] = 0; ct_snat(172.168.0.10);)
>>> > @@ -5799,6 +5827,7 @@ AT_CAPTURE_FILE([lr0flows])
>>> >
>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.10 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.20 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.30 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>> > @@ -5806,10 +5835,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.20 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);)
>>> >  ])
>>> >
>>> > @@ -5824,24 +5855,20 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src
>>> == 10.0.0.0/24 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>>> action=(ct_next(snat);)
>>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src
>>> == 10.0.0.10 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>>> action=(ct_next(snat);)
>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> > -  table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.dst
>>> == 10.0.0.0/24 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>> >    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src
>>> == 10.0.0.0/24 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>>> action=(ct_snat(172.168.0.10);)
>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>>> == 10.0.0.10 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>>> == 10.0.0.10 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>>> action=(ct_snat(172.168.0.30);)
>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>>> == 10.0.0.3 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>>> action=(ct_snat(172.168.0.20);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows], [0],
>>> [dnl
>>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_snat   ), priority=153  , match=(ip && ip4.dst
>>> == 10.0.0.0/24 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && ct.new),
>>> action=(ct_commit_to_zone(snat);)
>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>>> == 10.0.0.10 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && ct.new),
>>> action=(ct_commit_to_zone(snat);)
>>> >  ])
>>> >
>>> >  # Associate load balancer to lr0
>>> > @@ -5866,6 +5893,7 @@ AT_CAPTURE_FILE([lr0flows])
>>> >
>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.10 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
>>> action=(ct_snat_in_czone;)
>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.10 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
>>> flags.use_snat_zone == 1), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.20 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
>>> action=(ct_snat_in_czone;)
>>> > @@ -5876,6 +5904,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.10), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.100), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.200), action=(ct_dnat;)
>>> > @@ -5884,6 +5913,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.20 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.0.200 &&
>>> is_chassis_resident("cr-lr0-public")),
>>> action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 &&
>>> is_chassis_resident("cr-lr0-public")),
>>> action=(ct_lb_mark(backends=10.0.0.4:8080);)
>>> > @@ -5916,10 +5946,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> >    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src
>>> == 10.0.0.0/24 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>>> action=(ct_snat_in_czone(172.168.0.10);)
>>> >    table=??(lr_out_snat        ), priority=154  , match=(ip && ip4.src
>>> == 10.0.0.0/24 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl) && reg9[[4]]
>>> == 1), action=(reg9[[4]] = 0; ct_snat(172.168.0.10);)
>>> > @@ -5939,6 +5971,7 @@ AT_CAPTURE_FILE([lr0flows])
>>> >
>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.10 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.20 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.30 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>> > @@ -5946,6 +5979,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.10), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.100), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.200), action=(ct_dnat;)
>>> > @@ -5954,6 +5988,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.20 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);)
>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.0.200 &&
>>> is_chassis_resident("cr-lr0-public")),
>>> action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 &&
>>> is_chassis_resident("cr-lr0-public")),
>>> action=(ct_lb_mark(backends=10.0.0.4:8080);)
>>> > @@ -5982,24 +6017,20 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src
>>> == 10.0.0.0/24 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>>> action=(ct_next(snat);)
>>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip && ip4.src
>>> == 10.0.0.10 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>>> action=(ct_next(snat);)
>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> > -  table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.dst
>>> == 10.0.0.0/24 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>> >    table=??(lr_out_snat        ), priority=153  , match=(ip && ip4.src
>>> == 10.0.0.0/24 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>>> action=(ct_snat(172.168.0.10);)
>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>>> == 10.0.0.10 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>>> == 10.0.0.10 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>>> action=(ct_snat(172.168.0.30);)
>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>>> == 10.0.0.3 && outport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
>>> action=(ct_snat(172.168.0.20);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows], [0],
>>> [dnl
>>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_snat   ), priority=153  , match=(ip && ip4.dst
>>> == 10.0.0.0/24 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && ct.new),
>>> action=(ct_commit_to_zone(snat);)
>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>>> == 10.0.0.10 && inport == "lr0-public" &&
>>> is_chassis_resident("cr-lr0-public") && ct.new),
>>> action=(ct_commit_to_zone(snat);)
>>> >  ])
>>> >
>>> >  # Make the logical router as Gateway router
>>> > @@ -6013,6 +6044,7 @@ AT_CAPTURE_FILE([lr0flows])
>>> >
>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>>> == 172.168.0.10), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>>> == 172.168.0.20), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>>> == 172.168.0.30), action=(ct_snat;)
>>> > @@ -6020,6 +6052,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.10), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.100), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.200), action=(ct_dnat;)
>>> > @@ -6028,6 +6061,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.0.200),
>>> action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>>> action=(ct_lb_mark(backends=10.0.0.4:8080);)
>>> > @@ -6053,11 +6087,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
>>> ct.new), action=(ct_commit { } ; next; )
>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> >    table=??(lr_out_snat        ), priority=25   , match=(ip && ip4.src
>>> == 10.0.0.0/24 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.10);)
>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip && ip4.src
>>> == 10.0.0.10 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.168.0.30);)
>>> > @@ -6074,6 +6109,7 @@ AT_CAPTURE_FILE([lr0flows])
>>> >
>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>>> == 172.168.0.10), action=(ct_snat;)
>>> > @@ -6083,6 +6119,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.10), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.100), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.200), action=(ct_dnat;)
>>> > @@ -6091,6 +6128,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.0.200),
>>> action=(flags.force_snat_for_lb = 1;
>>> ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
>>> force_snat);)
>>> > @@ -6116,11 +6154,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
>>> ct.new), action=(ct_commit { } ; next; )
>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=110  ,
>>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
>>> action=(ct_snat(172.168.0.10);)
>>> >    table=??(lr_out_snat        ), priority=110  ,
>>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
>>> action=(ct_snat(10.0.0.1);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> > @@ -6138,6 +6177,7 @@ AT_CAPTURE_FILE([lr0flows])
>>> >
>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip && ip4.dst
>>> == 172.168.0.10), action=(ct_snat;)
>>> > @@ -6147,6 +6187,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.10), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.10), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.100), action=(ct_dnat;)
>>> > @@ -6156,6 +6197,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.0.200),
>>> action=(flags.force_snat_for_lb = 1;
>>> ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
>>> force_snat);)
>>> > @@ -6182,11 +6224,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
>>> ct.new), action=(ct_commit { } ; next; )
>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=110  ,
>>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
>>> action=(ct_snat(172.168.0.10);)
>>> >    table=??(lr_out_snat        ), priority=110  ,
>>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
>>> action=(ct_snat(10.0.0.1);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> > @@ -6212,6 +6255,7 @@ AT_CAPTURE_FILE([lr0flows])
>>> >
>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-public" && ip6.dst == def0::10), action=(ct_snat;)
>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
>>> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>>> > @@ -6223,6 +6267,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 10.0.0.10), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.10), action=(ct_dnat;)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.100), action=(ct_dnat;)
>>> > @@ -6233,6 +6278,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.0.200),
>>> action=(flags.force_snat_for_lb = 1;
>>> ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
>>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
>>> force_snat);)
>>> > @@ -6260,11 +6306,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
>>> ct.new), action=(ct_commit { } ; next; )
>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=110  ,
>>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
>>> action=(ct_snat(172.168.0.10);)
>>> >    table=??(lr_out_snat        ), priority=110  ,
>>> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
>>> action=(ct_snat(10.0.0.1);)
>>> >    table=??(lr_out_snat        ), priority=110  ,
>>> match=(flags.force_snat_for_lb == 1 && ip6 && outport == "lr0-public"),
>>> action=(ct_snat(def0::10);)
>>> > @@ -6291,15 +6338,18 @@ AT_CAPTURE_FILE([lr0flows])
>>> >
>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
>>> action=(flags.without_unsnat = 1; next;)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip && ip4.dst
>>> == 172.168.0.210), action=(ct_dnat;)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.0.210 && tcp && tcp.dst == 60),
>>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.50:6062,
>>> 10.0.0.60:6062; force_snat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.0.210 && udp && udp.dst == 60),
>>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.50:6062,
>>> 10.0.0.60:6062; force_snat);)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>> > @@ -6322,11 +6372,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
>>> ct.new), action=(ct_commit { } ; next; )
>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
>>> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>> >  ])
>>> >
>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip && ct.new
>>> && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
>>> action=(next;)
>>> >  ])
>>> >
>>> > @@ -6358,6 +6409,7 @@ check ovn-nbctl --wait=sb sync
>>> >
>>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
>>> ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.10.10), action=(reg0 = 0; reject {
>>> outport <-> inport; next(pipeline=egress,table=??);};)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
>>> !ct.est && !ct.new), action=(ct_commit_nat;)
>>> > @@ -6372,6 +6424,7 @@ check ovn-nbctl --wait=sb set load_balancer lb5
>>> options:skip_snat=true
>>> >
>>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
>>> ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.10.10), action=(flags.skip_snat_for_lb
>>> = 1; reg0 = 0; reject { outport <-> inport;
>>> next(pipeline=egress,table=??);};)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
>>> !ct.est && !ct.new), action=(ct_commit_nat;)
>>> > @@ -6388,6 +6441,7 @@ check ovn-nbctl --wait=sb set logical_router lr0
>>> options:lb_force_snat_ip="route
>>> >
>>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
>>> ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.10.10),
>>> action=(flags.force_snat_for_lb = 1; reg0 = 0; reject { outport <-> inport;
>>> next(pipeline=egress,table=??);};)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
>>> !ct.est && !ct.new), action=(ct_commit_nat;)
>>> > @@ -6405,6 +6459,7 @@ check ovn-nbctl --wait=sb lr-lb-add lr0 lb6
>>> >
>>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
>>> ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.10.30), action=(drop;)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
>>> !ct.est && !ct.new), action=(ct_commit_nat;)
>>> > @@ -6419,6 +6474,7 @@ check ovn-nbctl --wait=sb set load_balancer lb6
>>> options:skip_snat=true
>>> >
>>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
>>> ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.10.30), action=(flags.skip_snat_for_lb
>>> = 1; drop;)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
>>> !ct.est && !ct.new), action=(ct_commit_nat;)
>>> > @@ -6435,6 +6491,7 @@ check ovn-nbctl --wait=sb set logical_router lr0
>>> options:lb_force_snat_ip="route
>>> >
>>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
>>> ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.168.10.30),
>>> action=(flags.force_snat_for_lb = 1; drop;)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
>>> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
>>> !ct.est && !ct.new), action=(ct_commit_nat;)
>>> > @@ -7951,9 +8008,6 @@ AT_CHECK([grep lr_in_unsnat lrflows | grep
>>> ct_snat | ovn_strip_lflows], [0], [dn
>>> >  ])
>>> >
>>> >  AT_CHECK([grep lr_out_snat lrflows | grep ct_snat |
>>> ovn_strip_lflows], [0], [dnl
>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>>> == 20.0.0.10 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1")),
>>> action=(ct_snat;)
>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>>> == 20.0.0.10 && inport == "DR-S2" && is_chassis_resident("cr-DR-S2")),
>>> action=(ct_snat;)
>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.dst
>>> == 20.0.0.10 && inport == "DR-S3" && is_chassis_resident("cr-DR-S3")),
>>> action=(ct_snat;)
>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>>> == 20.0.0.10 && outport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>>> (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.10);)
>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>>> == 20.0.0.10 && outport == "DR-S2" && is_chassis_resident("cr-DR-S2") &&
>>> (!ct.trk || !ct.rpl)), action=(ct_snat(10.0.0.10);)
>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip && ip4.src
>>> == 20.0.0.10 && outport == "DR-S3" && is_chassis_resident("cr-DR-S3") &&
>>> (!ct.trk || !ct.rpl)), action=(ct_snat(192.168.0.10);)
>>> > @@ -7961,9 +8015,6 @@ AT_CHECK([grep lr_out_snat lrflows | grep
>>> ct_snat | ovn_strip_lflows], [0], [dnl
>>> >
>>> >  AT_CHECK([grep lr_out_post_snat lrflows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
>>> action=(next;)
>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>>> == 20.0.0.10 && inport == "DR-S1" && is_chassis_resident("cr-DR-S1") &&
>>> ct.new), action=(ct_commit_to_zone(snat);)
>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>>> == 20.0.0.10 && inport == "DR-S2" && is_chassis_resident("cr-DR-S2") &&
>>> ct.new), action=(ct_commit_to_zone(snat);)
>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip && ip4.dst
>>> == 20.0.0.10 && inport == "DR-S3" && is_chassis_resident("cr-DR-S3") &&
>>> ct.new), action=(ct_commit_to_zone(snat);)
>>> >  ])
>>> >
>>> >  check ovn-nbctl --wait=sb lr-nat-del DR snat 20.0.0.10
>>> > @@ -9387,6 +9438,7 @@ AT_CHECK([grep "lr_in_lb_aff_check" R1flows |
>>> ovn_strip_lflows], [0], [dnl
>>> >  ])
>>> >  AT_CHECK([grep "lr_in_dnat " R1flows | ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
>>> action=(ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);)
>>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] ==
>>> 1 && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] ==
>>> 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 && reg8[[0..15]]
>>> == 80), action=(reg0 = 172.16.0.10; ct_lb_mark(backends=20.0.0.2:80);)
>>> > @@ -9411,6 +9463,7 @@ AT_CAPTURE_FILE([R1flows_skip_snat])
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat " R1flows_skip_snat | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
>>> action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
>>> 20.0.0.2:80; skip_snat);)
>>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] ==
>>> 1 && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] ==
>>> 1 && ct.new && ip4.dst == 172.16.0.10 && 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);)
>>> > @@ -9432,6 +9485,7 @@ AT_CAPTURE_FILE([R1flows_force_snat])
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat " R1flows_force_snat | ovn_strip_lflows],
>>> [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
>>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
>>> 20.0.0.2:80; force_snat);)
>>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] ==
>>> 1 && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] ==
>>> 1 && ct.new && ip4.dst == 172.16.0.10 && 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);)
>>> > @@ -9452,6 +9506,7 @@ AT_CAPTURE_FILE([R1flows_force_skip_snat])
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat " R1flows_force_skip_snat |
>>> ovn_strip_lflows], [0], [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
>>> action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
>>> 20.0.0.2:80; skip_snat);)
>>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] ==
>>> 1 && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] ==
>>> 1 && ct.new && ip4.dst == 172.16.0.10 && 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);)
>>> > @@ -9476,6 +9531,7 @@ AT_CAPTURE_FILE([R1flows_2lbs])
>>> >
>>> >  AT_CHECK([grep "lr_in_dnat " R1flows_2lbs | ovn_strip_lflows], [0],
>>> [dnl
>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
>>> action=(next;)
>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
>>> ct.new), action=(ct_commit_to_zone(dnat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
>>> action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
>>> 20.0.0.2:80; skip_snat);)
>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
>>> !ct.rel && ip4 && ip4.dst == 172.16.0.20 && tcp && tcp.dst == 80),
>>> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
>>> 20.0.0.2:80; force_snat);)
>>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]] ==
>>> 1 && ct.new && ip4.dst == 172.16.0.10 && 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);)
>>> > diff --git a/tests/system-ovn.at b/tests/system-ovn.at
>>> > index 861b1cb99..dc7b0ab2e 100644
>>> > --- a/tests/system-ovn.at
>>> > +++ b/tests/system-ovn.at
>>> > @@ -117,6 +117,7 @@ NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2
>>> 30.0.0.2 | FORMAT_PING], \
>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.2) | \
>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>> >
>>>  icmp,orig=(src=172.16.1.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>> +icmp,orig=(src=172.16.1.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>>  icmp,orig=(src=172.16.1.2,dst=30.0.0.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >  ])
>>> >
>>> > @@ -297,6 +298,7 @@ NS_CHECK_EXEC([alice1], [ping6 -q -c 3 -i 0.3 -w 2
>>> fd30::2 | FORMAT_PING], \
>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd21::2) | \
>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>> >
>>>  icmpv6,orig=(src=fd21::2,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>>> >
>>> +icmpv6,orig=(src=fd21::2,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>>> >
>>>  icmpv6,orig=(src=fd21::2,dst=fd30::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>>> >  ])
>>> >
>>> > @@ -3753,6 +3755,7 @@ NS_CHECK_EXEC([foo2], [ping6 -q -c 3 -i 0.3 -w 2
>>> fd20::2 | FORMAT_PING], \
>>> >  ovs-appctl dpctl/dump-conntrack | grep icmpv6
>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd11::3) | \
>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>> >
>>> +icmpv6,orig=(src=fd11::3,dst=fd20::2,id=<cleared>,type=128,code=0),reply=(src=fd20::2,dst=fd11::3,id=<cleared>,type=129,code=0),zone=<cleared>
>>> >  ])
>>> >
>>> >  # We verify that SNAT indeed happened via 'dump-conntrack' command.
>>> > @@ -3938,6 +3941,8 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2
>>> 192.168.2.2 | FORMAT_PING], \
>>> >  # We verify that the connection is not tracked.
>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
>>> FORMAT_CT(192.168.2.2) | \
>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>> >
>>> +icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>> +icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >  ])
>>> >
>>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>>> > @@ -3950,6 +3955,8 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
>>> 192.168.2.2 | FORMAT_PING], \
>>> >  # We verify that the connection is not tracked.
>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
>>> FORMAT_CT(192.168.2.2) | \
>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>> >
>>> +icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>> +icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >  ])
>>> >
>>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>>> > @@ -3959,9 +3966,11 @@ NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2
>>> 192.168.1.3 | FORMAT_PING], \
>>> >  3 packets transmitted, 3 received, 0% packet loss, time 0ms
>>> >  ])
>>> >
>>> > -# We verify that the connection is not tracked.
>>> > +# We verify that the connection is tracked.
>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
>>> FORMAT_CT(192.168.2.2) | \
>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>> >
>>> +icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>> +icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >  ])
>>> >
>>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>>> > @@ -3978,6 +3987,7 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep
>>> icmp | FORMAT_CT(172.16.1.4) |
>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>> >
>>>  icmp,orig=(src=172.16.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>>  icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>> +icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >  ])
>>> >
>>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>>> > @@ -3993,7 +4003,6 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
>>> 172.16.1.4 | FORMAT_PING], \
>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
>>> FORMAT_CT(172.16.1.1) | \
>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>> >
>>>  icmp,orig=(src=172.16.1.1,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>> -icmp,orig=(src=172.16.1.1,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>>  icmp,orig=(src=192.168.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >  ])
>>> >
>>> > @@ -4144,6 +4153,7 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2
>>> fd20::4 | FORMAT_PING], \
>>> >  # Then DNAT of 'bar1' address happens (listed first below).
>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::4) | \
>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>> >
>>> +icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd11::2,id=<cleared>,type=129,code=0),zone=<cleared>
>>> >
>>>  icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
>>> >
>>>  icmpv6,orig=(src=fd20::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
>>> >  ])
>>> > @@ -4161,7 +4171,6 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2
>>> fd20::4 | FORMAT_PING], \
>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \
>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>> >
>>>  icmpv6,orig=(src=fd11::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>>> >
>>> -icmpv6,orig=(src=fd20::1,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>>> >
>>>  icmpv6,orig=(src=fd20::1,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>>> >  ])
>>> >
>>> > @@ -8682,10 +8691,10 @@ test_ping sw11 192.168.1.2
>>> >  OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep -v "n_packets=0" |
>>> grep 'nat(src=172.16.1.21)'])
>>> >  # Ensure conntrack entry is present
>>> >  OVS_WAIT_FOR_OUTPUT([
>>> > -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
>>> > +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>>> >        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>> >
>>> -icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>> -tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>>> >
>>> +icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>> +tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>>> >  ])
>>> >
>>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>>> > @@ -8697,9 +8706,11 @@ test_ping sw11 192.168.1.2
>>> >
>>> >  # Ensure conntrack entry is present
>>> >  OVS_WAIT_FOR_OUTPUT([
>>> > -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
>>> > +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>>> >        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>> >
>>> +icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>>  icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>> +tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>>> >
>>>  tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>>> >  ])
>>> >
>>> > @@ -8711,10 +8722,10 @@ test_ping sw11 172.16.1.2
>>> >
>>> >  # Ensure conntrack entry is present
>>> >  OVS_WAIT_FOR_OUTPUT([
>>> > -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
>>> > +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>>> >        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>> >
>>> -icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>> -tcp,orig=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>>> >
>>> +icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>>> >
>>> +tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>>> >  ])
>>> >
>>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>>> > --
>>> > 2.46.2
>>> >
>>> > _______________________________________________
>>> > dev mailing list
>>> > dev@openvswitch.org
>>> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>>
>>
>>
>> --
>>
>> Ales Musil
>>
>> Senior Software Engineer - OVN Core
>>
>> Red Hat EMEA <https://www.redhat.com>
>>
>> amusil@redhat.com
>> <https://red.ht/sig>
>>
>
Han Zhou Nov. 20, 2024, 5:24 a.m. UTC | #6
On Wed, Nov 13, 2024 at 11:59 PM Ales Musil <amusil@redhat.com> wrote:
>
>
>
> On Thu, Nov 14, 2024 at 8:35 AM Han Zhou <hzhou@ovn.org> wrote:
>>
>>
>>
>> On Wed, Nov 13, 2024 at 2:00 AM Ales Musil <amusil@redhat.com> wrote:
>>>
>>>
>>>
>>> On Wed, Nov 13, 2024 at 10:37 AM Han Zhou <hzhou@ovn.org> wrote:
>>>>
>>>> Thanks Ales.
>>>>
>>>
>>> Hi Han,
>>>
>>>>
>>>>
>>>> On Tue, Oct 8, 2024 at 3:12 AM Ales Musil <amusil@redhat.com> wrote:
>>>> >
>>>> > Commit all traffic that is not already commit by either NAT or LB.
This
>>>> > ensures that the traffic is tracked, and we don't erroneously commit
>>>> > reply traffic, or reply traffic is not marked as invalid.
>>>> >
>>>> > To achieve the commit we need to perform lookup on every packet
>>>> > that goes through LR pipeline whenever there is stateful NAT.
>>>> >
>>>> > The SNAT lookup requires additional flag as the unSNAT is happening
>>>> > in ingress pipeline and at that point we need to know if the packet
>>>> > is reply or not. This is not required for DNAT, because unDNAT stage
>>>> > happens in egress.
>>>>
>>>> Could you help explain in a little more detail how the ct.rpl relates
to this new without_unsnat flag? I still can't figure out why this flag is
needed.
>>>
>>>
>>> Without it we would commit reply traffic for SNAT which is wrong. That
caused all sorts of issues.
>>>
>>
>> I understand that we shouldn't commit reply traffic, but my question was
why does the "without_unsnat" flag avoid that. I don't see any link between
the without_unsnat and ct.rpl. It may be helpful to add comments in the
code as well.
>
>
> Sure I can add a comment to make it more obvious.
>

Hi Ales, after discussing with you on the IRC, I checked the code in more
detail and did some tests. So your point was that the current pipeline
should ensure only reply packets go through UNSNAT, and so if the
without_unsnat bit is NOT set, they must be reply direction packets. In
other words, you think without_unsnat == 1 should cover all packets of the
original direction. However, this doesn't seem to be true because the
current pipeline doesn't check if it is reply packets before going through
UNSNAT. It only checks if the destination IP belongs to any SNAT IP then
send to UNSNAT. An example that would break the assumption is
dnat_and_snat. The same IP is used for both SNAT and DNAT, so it is
possible that a client initiates connection to the DNAT IP, and the packet
of the original direction will be sent to the UNSNAT and in the end it
won't be committed because the without_unsnat bit is 0.

>>
>>
>>>>
>>>>
>>>> In addition, it seems that with this flag, you commit to snat zone
only if the packet's without_unsnat == 1, but even if the packet did
perform unSNAT we would still want to commit it to SNAT zone, because of
the HW offload issue reported here:
https://mail.openvswitch.org/pipermail/ovs-dev/2024-August/417025.html
>>>
>>>
>>> I'm probably missing something, why would we want to commit traffic
that did unSNAT? That traffic is already committed in the SNAT zone. The
issue described in the thread should be gone, AFAIU the problem was that
the traffic wasn't committed in the original direction, then unSNAT would
mark it as new. That shouldn't happen with this series.
>>>
>>
>> My understanding was, the lflow you added with priority 10 sets the
without_unsnat flag for the packets that didn't perform ct_snat at the
UNSNAT stage, and later you commit these to the SNAT zone. However, for the
other packets that performed the ct_snat at the UNSNAT stage, having
traversed the SNAT zone doesn't necessarily mean they were committed to the
zone, right? So with your change, if a packet was sent to the CT at the
UNSNAT stage, it would never get committed to SNAT zone, if not committed
at any other stages. Did I miss anything?
>
>
> The unSNAT rules should correspond to regular SNAT, in other words if we
have unSNAT for a certain match the same traffic pattern should match on
SNAT in the original direction. Unless I'm missing some case where the
original traffic goes through unSNAT but not SNAT it should be covered.
>

The same example of dnat_and_snat would break the assumption here, and
another example would be the issue mentioned in [0], because the same IP is
used for both SNAT and LB VIP (DNAT).

I used the system test "2 LRs connected via LS, gateway router, SNAT and
DNAT" to verify this by changing the nat rule from dnat to dnat_and_snat
for the external IP 30.0.0.2. And from ovn-trace:

ovn-trace alice 'inport == "alice1" && eth.src == f0:00:00:01:02:04 &&
ip4.src == 172.16.1.2 && eth.dst == 00:00:02:01:02:03 && ip4.dst ==
30.0.0.2 && ip.ttl == 32'

I can see that the without_unsnat bit is NOT set in this case, and the
entry is never committed to SNAT zone.

I think we shouldn't rely on checking dst IP v.s. SNAT/DNAT IPs to decide
if it is reply or not. We need to have generic steps such as:
1. go through SNAT/DNAT
2. if ct.new, commit to SNAT/DNAT

These steps are required for both SNAT and DNAT zones for every packet
whenever stateful NAT/LB is enabled for the datapath. I didn't figure out
the detailed change though. What do you think?

[0] https://mail.openvswitch.org/pipermail/ovs-dev/2024-August/417025.html

In addition, please see below a comment for the test case.

> Thinking about this there is one scenario that might be problematic and
that is a combination of stateful and stateless SNAT, the stateless SNAT
traffic wouldn't be committed. Is that what you had in mind or is it a
different scenario? In any case I can fix that with proper iteration by
setting this flag for stateless SNATs.
>
> Thanks,
> Ales
>
>>
>>
>> Thanks,
>> Han
>>
>>>>
>>>>
>>>> BTW, I have been trying my best but I feel it is quite difficult to
reason about the correctness of the DNAT/SNAT pipelines, which have become
so complex. I hope our test case coverage is sufficient for all kinds of
corner cases :)
>>>
>>>
>>> Yeah I understand and we probably have decent coverage because some of
my  thoughts were quickly disputed by failing and misbehaving tests :)
>>>>
>>>>
>>>> I am still working on performance test for this patch. Sorry it took
so long but I still need some more time.
>>>>
>>>
>>> This is one of the things that we shouldn't rush, thank you for looking
into the performance testing!
>>>
>>>>
>>>>
>>>> Regards,
>>>> Han
>>>>
>>>
>>> Regards,
>>> Ales
>>>
>>>>
>>>> >
>>>> > Signed-off-by: Ales Musil <amusil@redhat.com>
>>>> > ---
>>>> > There is one failing system test with userspace datapath, that's due
>>>> > to the recirculation limit that is being hit due to additional
>>>> > lookups.
>>>> > ---
>>>> >  include/ovn/logical-fields.h |   4 ++
>>>> >  lib/logical-fields.c         |   4 ++
>>>> >  northd/northd.c              |  76 ++++++++++++----------
>>>> >  tests/ovn-northd.at          | 118
++++++++++++++++++++++++++---------
>>>> >  tests/system-ovn.at          |  31 ++++++---
>>>> >  5 files changed, 158 insertions(+), 75 deletions(-)
>>>> >
>>>> > diff --git a/include/ovn/logical-fields.h
b/include/ovn/logical-fields.h
>>>> > index d6c4a9b6b..cc1f50ff2 100644
>>>> > --- a/include/ovn/logical-fields.h
>>>> > +++ b/include/ovn/logical-fields.h
>>>> > @@ -82,6 +82,7 @@ enum mff_log_flags_bits {
>>>> >      MLF_LOCALNET_BIT = 15,
>>>> >      MLF_RX_FROM_TUNNEL_BIT = 16,
>>>> >      MLF_ICMP_SNAT_BIT = 17,
>>>> > +    MLF_WITHOUT_UNSNAT_BIT = 18,
>>>> >  };
>>>> >
>>>> >  /* MFF_LOG_FLAGS_REG flag assignments */
>>>> > @@ -137,6 +138,9 @@ enum mff_log_flags {
>>>> >      MLF_RX_FROM_TUNNEL = (1 << MLF_RX_FROM_TUNNEL_BIT),
>>>> >
>>>> >      MLF_ICMP_SNAT = (1 << MLF_ICMP_SNAT_BIT),
>>>> > +
>>>> > +    /* Indicate that the packet didn't go through unSNAT. */
>>>> > +    MLF_WITHOUT_UNSNAT = (1 << MLF_WITHOUT_UNSNAT_BIT),
>>>> >  };
>>>> >
>>>> >  /* OVN logical fields
>>>> > diff --git a/lib/logical-fields.c b/lib/logical-fields.c
>>>> > index 5a8b53f2b..c63e19897 100644
>>>> > --- a/lib/logical-fields.c
>>>> > +++ b/lib/logical-fields.c
>>>> > @@ -139,6 +139,10 @@ ovn_init_symtab(struct shash *symtab)
>>>> >                               flags_str);
>>>> >      snprintf(flags_str, sizeof flags_str, "flags[%d]",
MLF_RX_FROM_TUNNEL_BIT);
>>>> >      expr_symtab_add_subfield(symtab, "flags.tunnel_rx", NULL,
flags_str);
>>>> > +    snprintf(flags_str, sizeof flags_str, "flags[%d]",
>>>> > +             MLF_WITHOUT_UNSNAT_BIT);
>>>> > +    expr_symtab_add_subfield(symtab, "flags.without_unsnat", NULL,
>>>> > +                             flags_str);
>>>> >
>>>> >      /* Connection tracking state. */
>>>> >      expr_symtab_add_field_scoped(symtab, "ct_mark", MFF_CT_MARK,
NULL, false,
>>>> > diff --git a/northd/northd.c b/northd/northd.c
>>>> > index 0364dd766..a42057e45 100644
>>>> > --- a/northd/northd.c
>>>> > +++ b/northd/northd.c
>>>> > @@ -15987,8 +15987,7 @@ build_lrouter_out_snat_flow(struct
lflow_table *lflows,
>>>> >                              struct ds *actions, bool
distributed_nat,
>>>> >                              struct eth_addr mac, int cidr_bits,
bool is_v6,
>>>> >                              struct ovn_port *l3dgw_port,
>>>> > -                            struct lflow_ref *lflow_ref,
>>>> > -                            const struct chassis_features *features)
>>>> > +                            struct lflow_ref *lflow_ref)
>>>> >  {
>>>> >      if (!(nat_entry->type == SNAT || nat_entry->type ==
DNAT_AND_SNAT)) {
>>>> >          return;
>>>> > @@ -16019,34 +16018,6 @@ build_lrouter_out_snat_flow(struct
lflow_table *lflows,
>>>> >                              priority, ds_cstr(match),
>>>> >                              ds_cstr(actions), &nat->header_,
>>>> >                              lflow_ref);
>>>> > -
>>>> > -    /* For the SNAT networks, we need to make sure that connections
are
>>>> > -     * properly tracked so we can decide whether to perform SNAT on
traffic
>>>> > -     * exiting the network. */
>>>> > -    if (features->ct_commit_to_zone && features->ct_next_zone &&
>>>> > -        nat_entry->type == SNAT && !od->is_gw_router) {
>>>> > -        /* For traffic that comes from SNAT network, initiate CT
state before
>>>> > -         * entering S_ROUTER_OUT_SNAT to allow matching on various
CT states.
>>>> > -         */
>>>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 70,
>>>> > -                      ds_cstr(match), "ct_next(snat);",
>>>> > -                      lflow_ref);
>>>> > -
>>>> > -        build_lrouter_out_snat_match(lflows, od, nat, match,
>>>> > -                                     distributed_nat, cidr_bits,
is_v6,
>>>> > -                                     l3dgw_port, lflow_ref, true);
>>>> > -
>>>> > -        /* New traffic that goes into SNAT network is committed to
CT to avoid
>>>> > -         * SNAT-ing replies.*/
>>>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, priority,
>>>> > -                      ds_cstr(match), "ct_snat;",
>>>> > -                      lflow_ref);
>>>> > -
>>>> > -        ds_put_cstr(match, " && ct.new");
>>>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_SNAT, priority,
>>>> > -                      ds_cstr(match), "ct_commit_to_zone(snat);",
>>>> > -                      lflow_ref);
>>>> > -    }
>>>> >  }
>>>> >
>>>> >  static void
>>>> > @@ -16439,9 +16410,6 @@ build_lrouter_nat_defrag_and_lb(
>>>> >          ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 50,
>>>> >                        "ip", "flags.loopback = 1; ct_dnat;",
>>>> >                        lflow_ref);
>>>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 50,
>>>> > -                      "ip && ct.new", "ct_commit { } ; next; ",
>>>> > -                      lflow_ref);
>>>> >      }
>>>> >
>>>> >      /* NAT rules are only valid on Gateway routers and routers with
>>>> > @@ -16459,6 +16427,9 @@ build_lrouter_nat_defrag_and_lb(
>>>> >
 !lport_addresses_is_empty(&lrnat_rec->dnat_force_snat_addrs);
>>>> >      bool lb_force_snat_ip =
>>>> >          !lport_addresses_is_empty(&lrnat_rec->lb_force_snat_addrs);
>>>> > +    bool stateful_dnat = lr_stateful_rec->has_lb_vip;
>>>> > +    bool stateful_snat = (dnat_force_snat_ip || lb_force_snat_ip ||
>>>> > +                          lrnat_rec->lb_force_snat_router_ip);
>>>> >
>>>> >      for (size_t i = 0; i < lrnat_rec->n_nat_entries; i++) {
>>>> >          struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i];
>>>> > @@ -16477,6 +16448,21 @@ build_lrouter_nat_defrag_and_lb(
>>>> >              continue;
>>>> >          }
>>>> >
>>>> > +        if (!stateless) {
>>>> > +            switch (nat_entry->type) {
>>>> > +            case DNAT:
>>>> > +                stateful_dnat = true;
>>>> > +                break;
>>>> > +            case SNAT:
>>>> > +                stateful_snat = true;
>>>> > +                break;
>>>> > +            case DNAT_AND_SNAT:
>>>> > +                stateful_snat = true;
>>>> > +                stateful_dnat = true;
>>>> > +                break;
>>>> > +            }
>>>> > +        }
>>>> > +
>>>> >          /* S_ROUTER_IN_UNSNAT
>>>> >           * Ingress UNSNAT table: It is for already established
connections'
>>>> >           * reverse traffic. i.e., SNAT has already been done in
egress
>>>> > @@ -16599,7 +16585,7 @@ build_lrouter_nat_defrag_and_lb(
>>>> >          } else {
>>>> >              build_lrouter_out_snat_flow(lflows, od, nat_entry,
match, actions,
>>>> >                                          distributed_nat, mac,
cidr_bits, is_v6,
>>>> > -                                        l3dgw_port, lflow_ref,
features);
>>>> > +                                        l3dgw_port, lflow_ref);
>>>> >          }
>>>> >
>>>> >          /* S_ROUTER_IN_ADMISSION - S_ROUTER_IN_IP_INPUT */
>>>> > @@ -16689,6 +16675,28 @@ build_lrouter_nat_defrag_and_lb(
>>>> >          }
>>>> >      }
>>>> >
>>>> > +
>>>> > +    bool can_commit = features->ct_commit_to_zone &&
features->ct_next_zone;
>>>> > +    if (can_commit && stateful_dnat) {
>>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 10,
>>>> > +                      "ip && (!ct.trk || !ct.rpl)",
>>>> > +                      "ct_next(dnat);", lflow_ref);
>>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 10,
>>>> > +                      "ip && ct.new", "ct_commit_to_zone(dnat);",
lflow_ref);
>>>> > +    }
>>>> > +
>>>> > +    if (can_commit && stateful_snat) {
>>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 10,
>>>> > +                      "ip", "flags.without_unsnat = 1; next;",
lflow_ref);
>>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 10,
>>>> > +                      "ip && (!ct.trk || !ct.rpl) && "
>>>> > +                      "flags.without_unsnat == 1", "ct_next(snat);",
>>>> > +                      lflow_ref);
>>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 10,
>>>> > +                      "ip && ct.new && flags.without_unsnat == 1",
>>>> > +                      "ct_commit_to_zone(snat);", lflow_ref);
>>>> > +    }
>>>> > +
>>>> >      if (use_common_zone && od->nbr->n_nat) {
>>>> >          ds_clear(match);
>>>> >          ds_put_cstr(match, "ip && ct_mark.natted == 1");
>>>> > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>>>> > index d6a8c4640..96e28a54a 100644
>>>> > --- a/tests/ovn-northd.at
>>>> > +++ b/tests/ovn-northd.at
>>>> > @@ -1181,18 +1181,18 @@ AT_CAPTURE_FILE([crflows])
>>>> >
>>>> >  AT_CHECK([grep -e "lr_out_snat" drflows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.dst == 50.0.0.11 && inport == "DR-S1" &&
is_chassis_resident("cr-DR-S1") && ip4.src == $allowed_range),
action=(ct_snat;)
>>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.src == 50.0.0.11 && outport == "DR-S1" &&
is_chassis_resident("cr-DR-S1") && ip4.dst == $allowed_range && (!ct.trk ||
!ct.rpl)), action=(ct_snat(172.16.1.1);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep -e "lr_out_post_snat" drflows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip &&
ip4.dst == 50.0.0.11 && inport == "DR-S1" &&
is_chassis_resident("cr-DR-S1") && ip4.src == $allowed_range && ct.new),
action=(ct_commit_to_zone(snat);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep -e "lr_out_snat" crflows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip &&
ip4.src == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.16.1.1);)
>>>> >  ])
>>>> > @@ -1220,19 +1220,19 @@ AT_CAPTURE_FILE([crflows2])
>>>> >
>>>> >  AT_CHECK([grep -e "lr_out_snat" drflows2 | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.dst == 50.0.0.11 && inport == "DR-S1" &&
is_chassis_resident("cr-DR-S1")), action=(ct_snat;)
>>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.src == 50.0.0.11 && outport == "DR-S1" &&
is_chassis_resident("cr-DR-S1") && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.16.1.1);)
>>>> >    table=??(lr_out_snat        ), priority=163  , match=(ip &&
ip4.src == 50.0.0.11 && outport == "DR-S1" &&
is_chassis_resident("cr-DR-S1") && ip4.dst == $disallowed_range),
action=(next;)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep -e "lr_out_post_snat" drflows2 | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip &&
ip4.dst == 50.0.0.11 && inport == "DR-S1" &&
is_chassis_resident("cr-DR-S1") && ct.new),
action=(ct_commit_to_zone(snat);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep -e "lr_out_snat" crflows2 | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip &&
ip4.src == 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
>>>> >    table=??(lr_out_snat        ), priority=35   , match=(ip &&
ip4.src == 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
>>>> > @@ -1259,6 +1259,7 @@ AT_CAPTURE_FILE([crflows2])
>>>> >
>>>> >  AT_CHECK([grep -e "lr_out_snat" drflows3 | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.src == 50.0.0.11 && outport == "DR-S1" &&
is_chassis_resident("cr-DR-S1") && ip4.dst == $allowed_range && (!ct.trk ||
!ct.rpl)), action=(ct_snat(172.16.1.2);)
>>>> >  ])
>>>> > @@ -1269,6 +1270,7 @@ AT_CHECK([grep -e "lr_out_post_snat" drflows3
| ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep -e "lr_out_snat" crflows3 | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip &&
ip4.src == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.16.1.2);)
>>>> >  ])
>>>> > @@ -1294,6 +1296,7 @@ AT_CAPTURE_FILE([crflows2])
>>>> >
>>>> >  AT_CHECK([grep -e "lr_out_snat" drflows4 | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.src == 50.0.0.11 && outport == "DR-S1" &&
is_chassis_resident("cr-DR-S1") && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.16.1.2);)
>>>> >    table=??(lr_out_snat        ), priority=163  , match=(ip &&
ip4.src == 50.0.0.11 && outport == "DR-S1" &&
is_chassis_resident("cr-DR-S1") && ip4.dst == $disallowed_range),
action=(next;)
>>>> > @@ -1301,6 +1304,7 @@ AT_CHECK([grep -e "lr_out_snat" drflows4 |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep -e "lr_out_snat" crflows4 | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip &&
ip4.src == 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
>>>> >    table=??(lr_out_snat        ), priority=35   , match=(ip &&
ip4.src == 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
>>>> > @@ -1663,6 +1667,7 @@ AT_CAPTURE_FILE([sbflows])
>>>> >  # dnat_and_snat or snat entry.
>>>> >  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip &&
ip4.dst == 192.168.2.1), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip &&
ip4.dst == 192.168.2.4), action=(ct_snat;)
>>>> >  ])
>>>> > @@ -1693,6 +1698,7 @@ AT_CAPTURE_FILE([sbflows])
>>>> >  # dnat_and_snat or snat entry.
>>>> >  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip &&
ip4.dst == 192.168.2.1), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip &&
ip4.dst == 192.168.2.4), action=(ct_snat;)
>>>> >  ])
>>>> > @@ -1801,6 +1807,7 @@ ovn-nbctl --wait=sb sync
>>>> >
>>>> >  AT_CHECK([ovn-sbctl lflow-list lr0 | grep lr_in_unsnat |
ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 &&
ip4.dst == 192.168.2.3), action=(ct_snat;)
>>>> >  ])
>>>> >
>>>> > @@ -4277,12 +4284,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.10), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.100), action=(ct_dnat;)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(ct_lb_mark(backends=10.0.0.4:8080);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
action=(ct_lb_mark(backends=10.0.0.40:8080);)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>>> > @@ -4302,18 +4311,21 @@ AT_CAPTURE_FILE([lr0flows])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 &&
ip4.dst == 20.0.0.4), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip6 &&
ip6.dst == aef0::4), action=(ct_snat;)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.10), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.100), action=(ct_dnat;)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
force_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
force_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>>> > @@ -4326,6 +4338,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=100  ,
match=(flags.force_snat_for_lb == 1 && ip4), action=(ct_snat(20.0.0.4);)
>>>> >    table=??(lr_out_snat        ), priority=100  ,
match=(flags.force_snat_for_lb == 1 && ip6), action=(ct_snat(aef0::4);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> > @@ -4339,7 +4352,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
ct.new), action=(ct_commit { } ; next; )
>>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>>> >  ])
>>>> >
>>>> >  check ovn-nbctl --wait=sb set logical_router lr0
options:lb_force_snat_ip="router_ip"
>>>> > @@ -4352,6 +4365,7 @@ AT_CHECK([grep "lr_in_ip_input" lr0flows |
grep "priority=60" | sort], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
>>>> > @@ -4359,12 +4373,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.10), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.100), action=(ct_dnat;)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
force_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
force_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>>> > @@ -4377,6 +4393,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
action=(ct_snat(172.168.0.100);)
>>>> >    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
action=(ct_snat(10.0.0.1);)
>>>> >    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
action=(ct_snat(20.0.0.1);)
>>>> > @@ -4391,7 +4408,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
ct.new), action=(ct_commit { } ; next; )
>>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>>> >  ])
>>>> >
>>>> >  check ovn-nbctl --wait=sb remove logical_router lr0 options chassis
>>>> > @@ -4416,6 +4433,7 @@ AT_CAPTURE_FILE([lr0flows])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
>>>> > @@ -4424,12 +4442,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.10), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.100), action=(ct_dnat;)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
force_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
force_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>>> > @@ -4442,6 +4462,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
action=(ct_snat(172.168.0.100);)
>>>> >    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
action=(ct_snat(10.0.0.1);)
>>>> >    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
action=(ct_snat(20.0.0.1);)
>>>> > @@ -4457,7 +4478,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
ct.new), action=(ct_commit { } ; next; )
>>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>>> >  ])
>>>> >
>>>> >  check ovn-nbctl --wait=sb lb-add lb2 10.0.0.20:80 10.0.0.40:8080
>>>> > @@ -4468,6 +4489,7 @@ ovn-sbctl dump-flows lr0 > lr0flows
>>>> >
>>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
>>>> > @@ -4476,6 +4498,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.100), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.20), action=(ct_dnat;)
>>>> >  ])
>>>> > @@ -4498,7 +4521,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
ct.new), action=(ct_commit { } ; next; )
>>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>>> >  ])
>>>> >
>>>> >  AT_CLEANUP
>>>> > @@ -5744,6 +5767,7 @@ AT_CAPTURE_FILE([lr0flows])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
action=(ct_snat_in_czone;)
>>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
flags.use_snat_zone == 1), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
action=(ct_snat_in_czone;)
>>>> > @@ -5754,10 +5778,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
>>>> >  ])
>>>> >
>>>> > @@ -5776,10 +5802,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> >    table=??(lr_out_snat        ), priority=153  , match=(ip &&
ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat_in_czone(172.168.0.10);)
>>>> >    table=??(lr_out_snat        ), priority=154  , match=(ip &&
ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl) && reg9[[4]]
== 1), action=(reg9[[4]] = 0; ct_snat(172.168.0.10);)
>>>> > @@ -5799,6 +5827,7 @@ AT_CAPTURE_FILE([lr0flows])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.30 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>>> > @@ -5806,10 +5835,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);)
>>>> >  ])
>>>> >
>>>> > @@ -5824,24 +5855,20 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip &&
ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_next(snat);)
>>>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip &&
ip4.src == 10.0.0.10 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_next(snat);)
>>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> > -  table=??(lr_out_snat        ), priority=153  , match=(ip &&
ip4.dst == 10.0.0.0/24 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>>> >    table=??(lr_out_snat        ), priority=153  , match=(ip &&
ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.168.0.10);)
>>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.dst == 10.0.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.src == 10.0.0.10 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.168.0.30);)
>>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.src == 10.0.0.3 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.168.0.20);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_snat   ), priority=153  , match=(ip &&
ip4.dst == 10.0.0.0/24 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && ct.new),
action=(ct_commit_to_zone(snat);)
>>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip &&
ip4.dst == 10.0.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && ct.new),
action=(ct_commit_to_zone(snat);)
>>>> >  ])
>>>> >
>>>> >  # Associate load balancer to lr0
>>>> > @@ -5866,6 +5893,7 @@ AT_CAPTURE_FILE([lr0flows])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
action=(ct_snat_in_czone;)
>>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
flags.use_snat_zone == 1), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
action=(ct_snat_in_czone;)
>>>> > @@ -5876,6 +5904,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.10), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.100), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.200), action=(ct_dnat;)
>>>> > @@ -5884,6 +5913,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
>>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.200 &&
is_chassis_resident("cr-lr0-public")),
action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 &&
is_chassis_resident("cr-lr0-public")),
action=(ct_lb_mark(backends=10.0.0.4:8080);)
>>>> > @@ -5916,10 +5946,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> >    table=??(lr_out_snat        ), priority=153  , match=(ip &&
ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat_in_czone(172.168.0.10);)
>>>> >    table=??(lr_out_snat        ), priority=154  , match=(ip &&
ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl) && reg9[[4]]
== 1), action=(reg9[[4]] = 0; ct_snat(172.168.0.10);)
>>>> > @@ -5939,6 +5971,7 @@ AT_CAPTURE_FILE([lr0flows])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.30 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>>> > @@ -5946,6 +5979,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.10), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.100), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.200), action=(ct_dnat;)
>>>> > @@ -5954,6 +5988,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);)
>>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.200 &&
is_chassis_resident("cr-lr0-public")),
action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 &&
is_chassis_resident("cr-lr0-public")),
action=(ct_lb_mark(backends=10.0.0.4:8080);)
>>>> > @@ -5982,24 +6017,20 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip &&
ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_next(snat);)
>>>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip &&
ip4.src == 10.0.0.10 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_next(snat);)
>>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> > -  table=??(lr_out_snat        ), priority=153  , match=(ip &&
ip4.dst == 10.0.0.0/24 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>>> >    table=??(lr_out_snat        ), priority=153  , match=(ip &&
ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.168.0.10);)
>>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.dst == 10.0.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
>>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.src == 10.0.0.10 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.168.0.30);)
>>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.src == 10.0.0.3 && outport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.168.0.20);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_snat   ), priority=153  , match=(ip &&
ip4.dst == 10.0.0.0/24 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && ct.new),
action=(ct_commit_to_zone(snat);)
>>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip &&
ip4.dst == 10.0.0.10 && inport == "lr0-public" &&
is_chassis_resident("cr-lr0-public") && ct.new),
action=(ct_commit_to_zone(snat);)
>>>> >  ])
>>>> >
>>>> >  # Make the logical router as Gateway router
>>>> > @@ -6013,6 +6044,7 @@ AT_CAPTURE_FILE([lr0flows])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip &&
ip4.dst == 172.168.0.10), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip &&
ip4.dst == 172.168.0.20), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip &&
ip4.dst == 172.168.0.30), action=(ct_snat;)
>>>> > @@ -6020,6 +6052,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.10), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.100), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.200), action=(ct_dnat;)
>>>> > @@ -6028,6 +6061,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.200),
action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(ct_lb_mark(backends=10.0.0.4:8080);)
>>>> > @@ -6053,11 +6087,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
ct.new), action=(ct_commit { } ; next; )
>>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> >    table=??(lr_out_snat        ), priority=25   , match=(ip &&
ip4.src == 10.0.0.0/24 && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.168.0.10);)
>>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip &&
ip4.src == 10.0.0.10 && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.168.0.30);)
>>>> > @@ -6074,6 +6109,7 @@ AT_CAPTURE_FILE([lr0flows])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip &&
ip4.dst == 172.168.0.10), action=(ct_snat;)
>>>> > @@ -6083,6 +6119,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.10), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.100), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.200), action=(ct_dnat;)
>>>> > @@ -6091,6 +6128,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.200),
action=(flags.force_snat_for_lb = 1;
ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
force_snat);)
>>>> > @@ -6116,11 +6154,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
ct.new), action=(ct_commit { } ; next; )
>>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
action=(ct_snat(172.168.0.10);)
>>>> >    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
action=(ct_snat(10.0.0.1);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> > @@ -6138,6 +6177,7 @@ AT_CAPTURE_FILE([lr0flows])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip &&
ip4.dst == 172.168.0.10), action=(ct_snat;)
>>>> > @@ -6147,6 +6187,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.10), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.10), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.100), action=(ct_dnat;)
>>>> > @@ -6156,6 +6197,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.200),
action=(flags.force_snat_for_lb = 1;
ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
force_snat);)
>>>> > @@ -6182,11 +6224,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
ct.new), action=(ct_commit { } ; next; )
>>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
action=(ct_snat(172.168.0.10);)
>>>> >    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
action=(ct_snat(10.0.0.1);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> > @@ -6212,6 +6255,7 @@ AT_CAPTURE_FILE([lr0flows])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip4.dst == 172.168.0.10), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-public" && ip6.dst == def0::10), action=(ct_snat;)
>>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
"lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
>>>> > @@ -6223,6 +6267,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 10.0.0.10), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.10), action=(ct_dnat;)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.100), action=(ct_dnat;)
>>>> > @@ -6233,6 +6278,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.20), action=(flags.loopback = 1; ct_dnat(10.0.0.3);)
>>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.200),
action=(flags.force_snat_for_lb = 1;
ct_lb_mark(backends=10.0.0.80,10.0.0.81; force_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
force_snat);)
>>>> > @@ -6260,11 +6306,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
ct.new), action=(ct_commit { } ; next; )
>>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
action=(ct_snat(172.168.0.10);)
>>>> >    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
action=(ct_snat(10.0.0.1);)
>>>> >    table=??(lr_out_snat        ), priority=110  ,
match=(flags.force_snat_for_lb == 1 && ip6 && outport == "lr0-public"),
action=(ct_snat(def0::10);)
>>>> > @@ -6291,15 +6338,18 @@ AT_CAPTURE_FILE([lr0flows])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
action=(flags.without_unsnat = 1; next;)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
>>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
ip4.dst == 172.168.0.210), action=(ct_dnat;)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.210 && tcp && tcp.dst == 60),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.50:6062,
10.0.0.60:6062; force_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.0.210 && udp && udp.dst == 60),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.50:6062,
10.0.0.60:6062; force_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>>> > @@ -6322,11 +6372,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
ct.new), action=(ct_commit { } ; next; )
>>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
(!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
>>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
action=(next;)
>>>> >  ])
>>>> >
>>>> > @@ -6358,6 +6409,7 @@ check ovn-nbctl --wait=sb sync
>>>> >
>>>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.10.10), action=(reg0 = 0; reject {
outport <-> inport; next(pipeline=egress,table=??);};)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
!ct.est && !ct.new), action=(ct_commit_nat;)
>>>> > @@ -6372,6 +6424,7 @@ check ovn-nbctl --wait=sb set load_balancer
lb5 options:skip_snat=true
>>>> >
>>>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.10.10), action=(flags.skip_snat_for_lb
= 1; reg0 = 0; reject { outport <-> inport;
next(pipeline=egress,table=??);};)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
!ct.est && !ct.new), action=(ct_commit_nat;)
>>>> > @@ -6388,6 +6441,7 @@ check ovn-nbctl --wait=sb set logical_router
lr0 options:lb_force_snat_ip="route
>>>> >
>>>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.10.10),
action=(flags.force_snat_for_lb = 1; reg0 = 0; reject { outport <-> inport;
next(pipeline=egress,table=??);};)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
!ct.est && !ct.new), action=(ct_commit_nat;)
>>>> > @@ -6405,6 +6459,7 @@ check ovn-nbctl --wait=sb lr-lb-add lr0 lb6
>>>> >
>>>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.10.30), action=(drop;)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
!ct.est && !ct.new), action=(ct_commit_nat;)
>>>> > @@ -6419,6 +6474,7 @@ check ovn-nbctl --wait=sb set load_balancer
lb6 options:skip_snat=true
>>>> >
>>>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.10.30), action=(flags.skip_snat_for_lb
= 1; drop;)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
!ct.est && !ct.new), action=(ct_commit_nat;)
>>>> > @@ -6435,6 +6491,7 @@ check ovn-nbctl --wait=sb set logical_router
lr0 options:lb_force_snat_ip="route
>>>> >
>>>> >  AT_CHECK([ovn-sbctl dump-flows lr0 | grep "lr_in_dnat" |
ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.168.10.30),
action=(flags.force_snat_for_lb = 1; drop;)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
!ct.rel && !ct.new && ct_mark.natted), action=(next;)
>>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.rel &&
!ct.est && !ct.new), action=(ct_commit_nat;)
>>>> > @@ -7951,9 +8008,6 @@ AT_CHECK([grep lr_in_unsnat lrflows | grep
ct_snat | ovn_strip_lflows], [0], [dn
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([grep lr_out_snat lrflows | grep ct_snat |
ovn_strip_lflows], [0], [dnl
>>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.dst == 20.0.0.10 && inport == "DR-S1" &&
is_chassis_resident("cr-DR-S1")), action=(ct_snat;)
>>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.dst == 20.0.0.10 && inport == "DR-S2" &&
is_chassis_resident("cr-DR-S2")), action=(ct_snat;)
>>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.dst == 20.0.0.10 && inport == "DR-S3" &&
is_chassis_resident("cr-DR-S3")), action=(ct_snat;)
>>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.src == 20.0.0.10 && outport == "DR-S1" &&
is_chassis_resident("cr-DR-S1") && (!ct.trk || !ct.rpl)),
action=(ct_snat(172.16.1.10);)
>>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.src == 20.0.0.10 && outport == "DR-S2" &&
is_chassis_resident("cr-DR-S2") && (!ct.trk || !ct.rpl)),
action=(ct_snat(10.0.0.10);)
>>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
ip4.src == 20.0.0.10 && outport == "DR-S3" &&
is_chassis_resident("cr-DR-S3") && (!ct.trk || !ct.rpl)),
action=(ct_snat(192.168.0.10);)
>>>> > @@ -7961,9 +8015,6 @@ AT_CHECK([grep lr_out_snat lrflows | grep
ct_snat | ovn_strip_lflows], [0], [dnl
>>>> >
>>>> >  AT_CHECK([grep lr_out_post_snat lrflows | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
action=(next;)
>>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip &&
ip4.dst == 20.0.0.10 && inport == "DR-S1" &&
is_chassis_resident("cr-DR-S1") && ct.new),
action=(ct_commit_to_zone(snat);)
>>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip &&
ip4.dst == 20.0.0.10 && inport == "DR-S2" &&
is_chassis_resident("cr-DR-S2") && ct.new),
action=(ct_commit_to_zone(snat);)
>>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip &&
ip4.dst == 20.0.0.10 && inport == "DR-S3" &&
is_chassis_resident("cr-DR-S3") && ct.new),
action=(ct_commit_to_zone(snat);)
>>>> >  ])
>>>> >
>>>> >  check ovn-nbctl --wait=sb lr-nat-del DR snat 20.0.0.10
>>>> > @@ -9387,6 +9438,7 @@ AT_CHECK([grep "lr_in_lb_aff_check" R1flows |
ovn_strip_lflows], [0], [dnl
>>>> >  ])
>>>> >  AT_CHECK([grep "lr_in_dnat " R1flows | ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
action=(ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);)
>>>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]]
== 1 && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]]
== 1 && ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 &&
reg8[[0..15]] == 80), action=(reg0 = 172.16.0.10;
ct_lb_mark(backends=20.0.0.2:80);)
>>>> > @@ -9411,6 +9463,7 @@ AT_CAPTURE_FILE([R1flows_skip_snat])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat " R1flows_skip_snat | ovn_strip_lflows],
[0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
20.0.0.2:80; skip_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]]
== 1 && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]]
== 1 && ct.new && ip4.dst == 172.16.0.10 && 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);)
>>>> > @@ -9432,6 +9485,7 @@ AT_CAPTURE_FILE([R1flows_force_snat])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat " R1flows_force_snat |
ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
20.0.0.2:80; force_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]]
== 1 && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]]
== 1 && ct.new && ip4.dst == 172.16.0.10 && 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);)
>>>> > @@ -9452,6 +9506,7 @@ AT_CAPTURE_FILE([R1flows_force_skip_snat])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat " R1flows_force_skip_snat |
ovn_strip_lflows], [0], [dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
20.0.0.2:80; skip_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]]
== 1 && ct.new && ip4.dst == 172.16.0.10 && 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=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]]
== 1 && ct.new && ip4.dst == 172.16.0.10 && 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);)
>>>> > @@ -9476,6 +9531,7 @@ AT_CAPTURE_FILE([R1flows_2lbs])
>>>> >
>>>> >  AT_CHECK([grep "lr_in_dnat " R1flows_2lbs | ovn_strip_lflows], [0],
[dnl
>>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
action=(next;)
>>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
ct.new), action=(ct_commit_to_zone(dnat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.16.0.10 && tcp && tcp.dst == 80),
action=(flags.skip_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
20.0.0.2:80; skip_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
!ct.rel && ip4 && ip4.dst == 172.16.0.20 && tcp && tcp.dst == 80),
action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.2:80,
20.0.0.2:80; force_snat);)
>>>> >    table=??(lr_in_dnat         ), priority=150  , match=(reg9[[6]]
== 1 && ct.new && ip4.dst == 172.16.0.10 && 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);)
>>>> > diff --git a/tests/system-ovn.at b/tests/system-ovn.at
>>>> > index 861b1cb99..dc7b0ab2e 100644
>>>> > --- a/tests/system-ovn.at
>>>> > +++ b/tests/system-ovn.at
>>>> > @@ -117,6 +117,7 @@ NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w
2 30.0.0.2 | FORMAT_PING], \
>>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.2) |
\
>>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>>> >
 icmp,orig=(src=172.16.1.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
+icmp,orig=(src=172.16.1.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
 icmp,orig=(src=172.16.1.2,dst=30.0.0.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >  ])

There should be 4 entries in CT, 2 in DNAT and 2 in CNAT, but FORMAT_CT
macro accidentally combined 2 of them. This causes inaccurate check of the
test result. For example, if we change the nat type to dnat_and_snat, the
current implementation doesn't commit to the SNAT zone, which ends up with
only 3 entries, which is wrong but the test case would still pass. I think
we should adjust the check so that it ensures there are 4 entries in CT.

Best regards,
Han

>>>> >
>>>> > @@ -297,6 +298,7 @@ NS_CHECK_EXEC([alice1], [ping6 -q -c 3 -i 0.3 -w
2 fd30::2 | FORMAT_PING], \
>>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd21::2) | \
>>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>>> >
 icmpv6,orig=(src=fd21::2,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>>>> >
+icmpv6,orig=(src=fd21::2,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>>>> >
 icmpv6,orig=(src=fd21::2,dst=fd30::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
>>>> >  ])
>>>> >
>>>> > @@ -3753,6 +3755,7 @@ NS_CHECK_EXEC([foo2], [ping6 -q -c 3 -i 0.3 -w
2 fd20::2 | FORMAT_PING], \
>>>> >  ovs-appctl dpctl/dump-conntrack | grep icmpv6
>>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd11::3) | \
>>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>>> >
+icmpv6,orig=(src=fd11::3,dst=fd20::2,id=<cleared>,type=128,code=0),reply=(src=fd20::2,dst=fd11::3,id=<cleared>,type=129,code=0),zone=<cleared>
>>>> >  ])
>>>> >
>>>> >  # We verify that SNAT indeed happened via 'dump-conntrack' command.
>>>> > @@ -3938,6 +3941,8 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w
2 192.168.2.2 | FORMAT_PING], \
>>>> >  # We verify that the connection is not tracked.
>>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
FORMAT_CT(192.168.2.2) | \
>>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>>> >
+icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
+icmp,orig=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>>>> > @@ -3950,6 +3955,8 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w
2 192.168.2.2 | FORMAT_PING], \
>>>> >  # We verify that the connection is not tracked.
>>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
FORMAT_CT(192.168.2.2) | \
>>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>>> >
+icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
+icmp,orig=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>>>> > @@ -3959,9 +3966,11 @@ NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w
2 192.168.1.3 | FORMAT_PING], \
>>>> >  3 packets transmitted, 3 received, 0% packet loss, time 0ms
>>>> >  ])
>>>> >
>>>> > -# We verify that the connection is not tracked.
>>>> > +# We verify that the connection is tracked.
>>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
FORMAT_CT(192.168.2.2) | \
>>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>>> >
+icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
+icmp,orig=(src=192.168.2.2,dst=192.168.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.3,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>>>> > @@ -3978,6 +3987,7 @@ AT_CHECK([ovs-appctl dpctl/dump-conntrack |
grep icmp | FORMAT_CT(172.16.1.4) |
>>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>>> >
 icmp,orig=(src=172.16.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
 icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
+icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=192.168.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>>>> > @@ -3993,7 +4003,6 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w
2 172.16.1.4 | FORMAT_PING], \
>>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
FORMAT_CT(172.16.1.1) | \
>>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>>> >
 icmp,orig=(src=172.16.1.1,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
-icmp,orig=(src=172.16.1.1,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
 icmp,orig=(src=192.168.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >  ])
>>>> >
>>>> > @@ -4144,6 +4153,7 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w
2 fd20::4 | FORMAT_PING], \
>>>> >  # Then DNAT of 'bar1' address happens (listed first below).
>>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::4) | \
>>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>>> >
+icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd11::2,id=<cleared>,type=129,code=0),zone=<cleared>
>>>> >
 icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
>>>> >
 icmpv6,orig=(src=fd20::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
>>>> >  ])
>>>> > @@ -4161,7 +4171,6 @@ NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w
2 fd20::4 | FORMAT_PING], \
>>>> >  AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \
>>>> >  sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>>> >
 icmpv6,orig=(src=fd11::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>>>> >
-icmpv6,orig=(src=fd20::1,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>>>> >
 icmpv6,orig=(src=fd20::1,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
>>>> >  ])
>>>> >
>>>> > @@ -8682,10 +8691,10 @@ test_ping sw11 192.168.1.2
>>>> >  OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep -v "n_packets=0"
| grep 'nat(src=172.16.1.21)'])
>>>> >  # Ensure conntrack entry is present
>>>> >  OVS_WAIT_FOR_OUTPUT([
>>>> > -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
>>>> > +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>>>> >        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>>> >
-icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
-tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>>>> >
+icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
+tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>>>> > @@ -8697,9 +8706,11 @@ test_ping sw11 192.168.1.2
>>>> >
>>>> >  # Ensure conntrack entry is present
>>>> >  OVS_WAIT_FOR_OUTPUT([
>>>> > -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
>>>> > +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>>>> >        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>>> >
+icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
 icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
+tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>>>> >
 tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>>>> >  ])
>>>> >
>>>> > @@ -8711,10 +8722,10 @@ test_ping sw11 172.16.1.2
>>>> >
>>>> >  # Ensure conntrack entry is present
>>>> >  OVS_WAIT_FOR_OUTPUT([
>>>> > -    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
>>>> > +    ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.1.2) | \
>>>> >        sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
>>>> >
-icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
-tcp,orig=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>>>> >
+icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.21,id=<cleared>,type=0,code=0),zone=<cleared>
>>>> >
+tcp,orig=(src=192.168.2.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.21,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
>>>> >  ])
>>>> >
>>>> >  AT_CHECK([ovs-appctl dpctl/flush-conntrack])
>>>> > --
>>>> > 2.46.2
>>>> >
>>>> > _______________________________________________
>>>> > dev mailing list
>>>> > dev@openvswitch.org
>>>> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>>
>>>
>>>
>>> --
>>>
>>> Ales Musil
>>>
>>> Senior Software Engineer - OVN Core
>>>
>>> Red Hat EMEA
>>>
>>> amusil@redhat.com
>
>
>
> --
>
> Ales Musil
>
> Senior Software Engineer - OVN Core
>
> Red Hat EMEA
>
> amusil@redhat.com

>
Ales Musil Nov. 20, 2024, 7:03 a.m. UTC | #7
On Wed, Nov 20, 2024 at 6:24 AM Han Zhou <hzhou@ovn.org> wrote:

>
>
> On Wed, Nov 13, 2024 at 11:59 PM Ales Musil <amusil@redhat.com> wrote:
> >
> >
> >
> > On Thu, Nov 14, 2024 at 8:35 AM Han Zhou <hzhou@ovn.org> wrote:
> >>
> >>
> >>
> >> On Wed, Nov 13, 2024 at 2:00 AM Ales Musil <amusil@redhat.com> wrote:
> >>>
> >>>
> >>>
> >>> On Wed, Nov 13, 2024 at 10:37 AM Han Zhou <hzhou@ovn.org> wrote:
> >>>>
> >>>> Thanks Ales.
> >>>>
> >>>
> >>> Hi Han,
> >>>
> >>>>
> >>>>
> >>>> On Tue, Oct 8, 2024 at 3:12 AM Ales Musil <amusil@redhat.com> wrote:
> >>>> >
> >>>> > Commit all traffic that is not already commit by either NAT or LB.
> This
> >>>> > ensures that the traffic is tracked, and we don't erroneously commit
> >>>> > reply traffic, or reply traffic is not marked as invalid.
> >>>> >
> >>>> > To achieve the commit we need to perform lookup on every packet
> >>>> > that goes through LR pipeline whenever there is stateful NAT.
> >>>> >
> >>>> > The SNAT lookup requires additional flag as the unSNAT is happening
> >>>> > in ingress pipeline and at that point we need to know if the packet
> >>>> > is reply or not. This is not required for DNAT, because unDNAT stage
> >>>> > happens in egress.
> >>>>
> >>>> Could you help explain in a little more detail how the ct.rpl relates
> to this new without_unsnat flag? I still can't figure out why this flag is
> needed.
> >>>
> >>>
> >>> Without it we would commit reply traffic for SNAT which is wrong. That
> caused all sorts of issues.
> >>>
> >>
> >> I understand that we shouldn't commit reply traffic, but my question
> was why does the "without_unsnat" flag avoid that. I don't see any link
> between the without_unsnat and ct.rpl. It may be helpful to add comments in
> the code as well.
> >
> >
> > Sure I can add a comment to make it more obvious.
> >
>
> Hi Ales, after discussing with you on the IRC, I checked the code in more
> detail and did some tests. So your point was that the current pipeline
> should ensure only reply packets go through UNSNAT, and so if the
> without_unsnat bit is NOT set, they must be reply direction packets. In
> other words, you think without_unsnat == 1 should cover all packets of the
> original direction. However, this doesn't seem to be true because the
> current pipeline doesn't check if it is reply packets before going through
> UNSNAT. It only checks if the destination IP belongs to any SNAT IP then
> send to UNSNAT. An example that would break the assumption is
> dnat_and_snat. The same IP is used for both SNAT and DNAT, so it is
> possible that a client initiates connection to the DNAT IP, and the packet
> of the original direction will be sent to the UNSNAT and in the end it
> won't be committed because the without_unsnat bit is 0.
>


Hi Han,
thank you for looking into this. That makes sense and is a bit unfortunate,
checking and propagating if the SNAT traffic is really reply or not would
need us to move the flow into stage after ct_snat, because we don't have
the ct state without sending it through the ct_snat/ct_next(snat) action
first.


> >>
> >>
> >>>>
> >>>>
> >>>> In addition, it seems that with this flag, you commit to snat zone
> only if the packet's without_unsnat == 1, but even if the packet did
> perform unSNAT we would still want to commit it to SNAT zone, because of
> the HW offload issue reported here:
> https://mail.openvswitch.org/pipermail/ovs-dev/2024-August/417025.html
> >>>
> >>>
> >>> I'm probably missing something, why would we want to commit traffic
> that did unSNAT? That traffic is already committed in the SNAT zone. The
> issue described in the thread should be gone, AFAIU the problem was that
> the traffic wasn't committed in the original direction, then unSNAT would
> mark it as new. That shouldn't happen with this series.
> >>>
> >>
> >> My understanding was, the lflow you added with priority 10 sets the
> without_unsnat flag for the packets that didn't perform ct_snat at the
> UNSNAT stage, and later you commit these to the SNAT zone. However, for the
> other packets that performed the ct_snat at the UNSNAT stage, having
> traversed the SNAT zone doesn't necessarily mean they were committed to the
> zone, right? So with your change, if a packet was sent to the CT at the
> UNSNAT stage, it would never get committed to SNAT zone, if not committed
> at any other stages. Did I miss anything?
> >
> >
> > The unSNAT rules should correspond to regular SNAT, in other words if we
> have unSNAT for a certain match the same traffic pattern should match on
> SNAT in the original direction. Unless I'm missing some case where the
> original traffic goes through unSNAT but not SNAT it should be covered.
> >
>
> The same example of dnat_and_snat would break the assumption here, and
> another example would be the issue mentioned in [0], because the same IP is
> used for both SNAT and LB VIP (DNAT).
>
> I used the system test "2 LRs connected via LS, gateway router, SNAT and
> DNAT" to verify this by changing the nat rule from dnat to dnat_and_snat
> for the external IP 30.0.0.2. And from ovn-trace:
>
> ovn-trace alice 'inport == "alice1" && eth.src == f0:00:00:01:02:04 &&
> ip4.src == 172.16.1.2 && eth.dst == 00:00:02:01:02:03 && ip4.dst ==
> 30.0.0.2 && ip.ttl == 32'
>
> I can see that the without_unsnat bit is NOT set in this case, and the
> entry is never committed to SNAT zone.
>
> I think we shouldn't rely on checking dst IP v.s. SNAT/DNAT IPs to decide
> if it is reply or not. We need to have generic steps such as:
> 1. go through SNAT/DNAT
> 2. if ct.new, commit to SNAT/DNAT
>
> These steps are required for both SNAT and DNAT zones for every packet
> whenever stateful NAT/LB is enabled for the datapath. I didn't figure out
> the detailed change though. What do you think?
>

Yeah that makes sense and for DNAT it should be happening with the change
as is, the SNAT is more tricky, I'll try to find out a way how to propagate
the ct.new flag across the pipeline. I feel like we will need a new stage
for that.


>
> [0] https://mail.openvswitch.org/pipermail/ovs-dev/2024-August/417025.html
>
> In addition, please see below a comment for the test case.
>

Thanks,
Ales


>
> > Thinking about this there is one scenario that might be problematic and
> that is a combination of stateful and stateless SNAT, the stateless SNAT
> traffic wouldn't be committed. Is that what you had in mind or is it a
> different scenario? In any case I can fix that with proper iteration by
> setting this flag for stateless SNATs.
> >
> > Thanks,
> > Ales
> >
> >>
> >>
> >> Thanks,
> >> Han
> >>
> >>>>
> >>>>
> >>>> BTW, I have been trying my best but I feel it is quite difficult to
> reason about the correctness of the DNAT/SNAT pipelines, which have become
> so complex. I hope our test case coverage is sufficient for all kinds of
> corner cases :)
> >>>
> >>>
> >>> Yeah I understand and we probably have decent coverage because some of
> my  thoughts were quickly disputed by failing and misbehaving tests :)
> >>>>
> >>>>
> >>>> I am still working on performance test for this patch. Sorry it took
> so long but I still need some more time.
> >>>>
> >>>
> >>> This is one of the things that we shouldn't rush, thank you for
> looking into the performance testing!
> >>>
> >>>>
> >>>>
> >>>> Regards,
> >>>> Han
> >>>>
> >>>
> >>> Regards,
> >>> Ales
> >>>
> >>>>
> >>>> >
> >>>> > Signed-off-by: Ales Musil <amusil@redhat.com>
> >>>> > ---
> >>>> > There is one failing system test with userspace datapath, that's due
> >>>> > to the recirculation limit that is being hit due to additional
> >>>> > lookups.
> >>>> > ---
> >>>> >  include/ovn/logical-fields.h |   4 ++
> >>>> >  lib/logical-fields.c         |   4 ++
> >>>> >  northd/northd.c              |  76 ++++++++++++----------
> >>>> >  tests/ovn-northd.at          | 118
> ++++++++++++++++++++++++++---------
> >>>> >  tests/system-ovn.at          |  31 ++++++---
> >>>> >  5 files changed, 158 insertions(+), 75 deletions(-)
> >>>> >
> >>>> > diff --git a/include/ovn/logical-fields.h
> b/include/ovn/logical-fields.h
> >>>> > index d6c4a9b6b..cc1f50ff2 100644
> >>>> > --- a/include/ovn/logical-fields.h
> >>>> > +++ b/include/ovn/logical-fields.h
> >>>> > @@ -82,6 +82,7 @@ enum mff_log_flags_bits {
> >>>> >      MLF_LOCALNET_BIT = 15,
> >>>> >      MLF_RX_FROM_TUNNEL_BIT = 16,
> >>>> >      MLF_ICMP_SNAT_BIT = 17,
> >>>> > +    MLF_WITHOUT_UNSNAT_BIT = 18,
> >>>> >  };
> >>>> >
> >>>> >  /* MFF_LOG_FLAGS_REG flag assignments */
> >>>> > @@ -137,6 +138,9 @@ enum mff_log_flags {
> >>>> >      MLF_RX_FROM_TUNNEL = (1 << MLF_RX_FROM_TUNNEL_BIT),
> >>>> >
> >>>> >      MLF_ICMP_SNAT = (1 << MLF_ICMP_SNAT_BIT),
> >>>> > +
> >>>> > +    /* Indicate that the packet didn't go through unSNAT. */
> >>>> > +    MLF_WITHOUT_UNSNAT = (1 << MLF_WITHOUT_UNSNAT_BIT),
> >>>> >  };
> >>>> >
> >>>> >  /* OVN logical fields
> >>>> > diff --git a/lib/logical-fields.c b/lib/logical-fields.c
> >>>> > index 5a8b53f2b..c63e19897 100644
> >>>> > --- a/lib/logical-fields.c
> >>>> > +++ b/lib/logical-fields.c
> >>>> > @@ -139,6 +139,10 @@ ovn_init_symtab(struct shash *symtab)
> >>>> >                               flags_str);
> >>>> >      snprintf(flags_str, sizeof flags_str, "flags[%d]",
> MLF_RX_FROM_TUNNEL_BIT);
> >>>> >      expr_symtab_add_subfield(symtab, "flags.tunnel_rx", NULL,
> flags_str);
> >>>> > +    snprintf(flags_str, sizeof flags_str, "flags[%d]",
> >>>> > +             MLF_WITHOUT_UNSNAT_BIT);
> >>>> > +    expr_symtab_add_subfield(symtab, "flags.without_unsnat", NULL,
> >>>> > +                             flags_str);
> >>>> >
> >>>> >      /* Connection tracking state. */
> >>>> >      expr_symtab_add_field_scoped(symtab, "ct_mark", MFF_CT_MARK,
> NULL, false,
> >>>> > diff --git a/northd/northd.c b/northd/northd.c
> >>>> > index 0364dd766..a42057e45 100644
> >>>> > --- a/northd/northd.c
> >>>> > +++ b/northd/northd.c
> >>>> > @@ -15987,8 +15987,7 @@ build_lrouter_out_snat_flow(struct
> lflow_table *lflows,
> >>>> >                              struct ds *actions, bool
> distributed_nat,
> >>>> >                              struct eth_addr mac, int cidr_bits,
> bool is_v6,
> >>>> >                              struct ovn_port *l3dgw_port,
> >>>> > -                            struct lflow_ref *lflow_ref,
> >>>> > -                            const struct chassis_features
> *features)
> >>>> > +                            struct lflow_ref *lflow_ref)
> >>>> >  {
> >>>> >      if (!(nat_entry->type == SNAT || nat_entry->type ==
> DNAT_AND_SNAT)) {
> >>>> >          return;
> >>>> > @@ -16019,34 +16018,6 @@ build_lrouter_out_snat_flow(struct
> lflow_table *lflows,
> >>>> >                              priority, ds_cstr(match),
> >>>> >                              ds_cstr(actions), &nat->header_,
> >>>> >                              lflow_ref);
> >>>> > -
> >>>> > -    /* For the SNAT networks, we need to make sure that
> connections are
> >>>> > -     * properly tracked so we can decide whether to perform SNAT
> on traffic
> >>>> > -     * exiting the network. */
> >>>> > -    if (features->ct_commit_to_zone && features->ct_next_zone &&
> >>>> > -        nat_entry->type == SNAT && !od->is_gw_router) {
> >>>> > -        /* For traffic that comes from SNAT network, initiate CT
> state before
> >>>> > -         * entering S_ROUTER_OUT_SNAT to allow matching on various
> CT states.
> >>>> > -         */
> >>>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 70,
> >>>> > -                      ds_cstr(match), "ct_next(snat);",
> >>>> > -                      lflow_ref);
> >>>> > -
> >>>> > -        build_lrouter_out_snat_match(lflows, od, nat, match,
> >>>> > -                                     distributed_nat, cidr_bits,
> is_v6,
> >>>> > -                                     l3dgw_port, lflow_ref, true);
> >>>> > -
> >>>> > -        /* New traffic that goes into SNAT network is committed to
> CT to avoid
> >>>> > -         * SNAT-ing replies.*/
> >>>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, priority,
> >>>> > -                      ds_cstr(match), "ct_snat;",
> >>>> > -                      lflow_ref);
> >>>> > -
> >>>> > -        ds_put_cstr(match, " && ct.new");
> >>>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_SNAT, priority,
> >>>> > -                      ds_cstr(match), "ct_commit_to_zone(snat);",
> >>>> > -                      lflow_ref);
> >>>> > -    }
> >>>> >  }
> >>>> >
> >>>> >  static void
> >>>> > @@ -16439,9 +16410,6 @@ build_lrouter_nat_defrag_and_lb(
> >>>> >          ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 50,
> >>>> >                        "ip", "flags.loopback = 1; ct_dnat;",
> >>>> >                        lflow_ref);
> >>>> > -        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 50,
> >>>> > -                      "ip && ct.new", "ct_commit { } ; next; ",
> >>>> > -                      lflow_ref);
> >>>> >      }
> >>>> >
> >>>> >      /* NAT rules are only valid on Gateway routers and routers with
> >>>> > @@ -16459,6 +16427,9 @@ build_lrouter_nat_defrag_and_lb(
> >>>> >
>  !lport_addresses_is_empty(&lrnat_rec->dnat_force_snat_addrs);
> >>>> >      bool lb_force_snat_ip =
> >>>> >          !lport_addresses_is_empty(&lrnat_rec->lb_force_snat_addrs);
> >>>> > +    bool stateful_dnat = lr_stateful_rec->has_lb_vip;
> >>>> > +    bool stateful_snat = (dnat_force_snat_ip || lb_force_snat_ip ||
> >>>> > +                          lrnat_rec->lb_force_snat_router_ip);
> >>>> >
> >>>> >      for (size_t i = 0; i < lrnat_rec->n_nat_entries; i++) {
> >>>> >          struct ovn_nat *nat_entry = &lrnat_rec->nat_entries[i];
> >>>> > @@ -16477,6 +16448,21 @@ build_lrouter_nat_defrag_and_lb(
> >>>> >              continue;
> >>>> >          }
> >>>> >
> >>>> > +        if (!stateless) {
> >>>> > +            switch (nat_entry->type) {
> >>>> > +            case DNAT:
> >>>> > +                stateful_dnat = true;
> >>>> > +                break;
> >>>> > +            case SNAT:
> >>>> > +                stateful_snat = true;
> >>>> > +                break;
> >>>> > +            case DNAT_AND_SNAT:
> >>>> > +                stateful_snat = true;
> >>>> > +                stateful_dnat = true;
> >>>> > +                break;
> >>>> > +            }
> >>>> > +        }
> >>>> > +
> >>>> >          /* S_ROUTER_IN_UNSNAT
> >>>> >           * Ingress UNSNAT table: It is for already established
> connections'
> >>>> >           * reverse traffic. i.e., SNAT has already been done in
> egress
> >>>> > @@ -16599,7 +16585,7 @@ build_lrouter_nat_defrag_and_lb(
> >>>> >          } else {
> >>>> >              build_lrouter_out_snat_flow(lflows, od, nat_entry,
> match, actions,
> >>>> >                                          distributed_nat, mac,
> cidr_bits, is_v6,
> >>>> > -                                        l3dgw_port, lflow_ref,
> features);
> >>>> > +                                        l3dgw_port, lflow_ref);
> >>>> >          }
> >>>> >
> >>>> >          /* S_ROUTER_IN_ADMISSION - S_ROUTER_IN_IP_INPUT */
> >>>> > @@ -16689,6 +16675,28 @@ build_lrouter_nat_defrag_and_lb(
> >>>> >          }
> >>>> >      }
> >>>> >
> >>>> > +
> >>>> > +    bool can_commit = features->ct_commit_to_zone &&
> features->ct_next_zone;
> >>>> > +    if (can_commit && stateful_dnat) {
> >>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 10,
> >>>> > +                      "ip && (!ct.trk || !ct.rpl)",
> >>>> > +                      "ct_next(dnat);", lflow_ref);
> >>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 10,
> >>>> > +                      "ip && ct.new", "ct_commit_to_zone(dnat);",
> lflow_ref);
> >>>> > +    }
> >>>> > +
> >>>> > +    if (can_commit && stateful_snat) {
> >>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 10,
> >>>> > +                      "ip", "flags.without_unsnat = 1; next;",
> lflow_ref);
> >>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 10,
> >>>> > +                      "ip && (!ct.trk || !ct.rpl) && "
> >>>> > +                      "flags.without_unsnat == 1",
> "ct_next(snat);",
> >>>> > +                      lflow_ref);
> >>>> > +        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 10,
> >>>> > +                      "ip && ct.new && flags.without_unsnat == 1",
> >>>> > +                      "ct_commit_to_zone(snat);", lflow_ref);
> >>>> > +    }
> >>>> > +
> >>>> >      if (use_common_zone && od->nbr->n_nat) {
> >>>> >          ds_clear(match);
> >>>> >          ds_put_cstr(match, "ip && ct_mark.natted == 1");
> >>>> > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> >>>> > index d6a8c4640..96e28a54a 100644
> >>>> > --- a/tests/ovn-northd.at
> >>>> > +++ b/tests/ovn-northd.at
> >>>> > @@ -1181,18 +1181,18 @@ AT_CAPTURE_FILE([crflows])
> >>>> >
> >>>> >  AT_CHECK([grep -e "lr_out_snat" drflows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip &&
> ip4.dst == 50.0.0.11 && inport == "DR-S1" &&
> is_chassis_resident("cr-DR-S1") && ip4.src == $allowed_range),
> action=(ct_snat;)
> >>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
> ip4.src == 50.0.0.11 && outport == "DR-S1" &&
> is_chassis_resident("cr-DR-S1") && ip4.dst == $allowed_range && (!ct.trk ||
> !ct.rpl)), action=(ct_snat(172.16.1.1);)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep -e "lr_out_post_snat" drflows | ovn_strip_lflows],
> [0], [dnl
> >>>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
> action=(next;)
> >>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip &&
> ip4.dst == 50.0.0.11 && inport == "DR-S1" &&
> is_chassis_resident("cr-DR-S1") && ip4.src == $allowed_range && ct.new),
> action=(ct_commit_to_zone(snat);)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep -e "lr_out_snat" crflows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip &&
> ip4.src == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.16.1.1);)
> >>>> >  ])
> >>>> > @@ -1220,19 +1220,19 @@ AT_CAPTURE_FILE([crflows2])
> >>>> >
> >>>> >  AT_CHECK([grep -e "lr_out_snat" drflows2 | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip &&
> ip4.dst == 50.0.0.11 && inport == "DR-S1" &&
> is_chassis_resident("cr-DR-S1")), action=(ct_snat;)
> >>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
> ip4.src == 50.0.0.11 && outport == "DR-S1" &&
> is_chassis_resident("cr-DR-S1") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.16.1.1);)
> >>>> >    table=??(lr_out_snat        ), priority=163  , match=(ip &&
> ip4.src == 50.0.0.11 && outport == "DR-S1" &&
> is_chassis_resident("cr-DR-S1") && ip4.dst == $disallowed_range),
> action=(next;)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep -e "lr_out_post_snat" drflows2 | ovn_strip_lflows],
> [0], [dnl
> >>>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
> action=(next;)
> >>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip &&
> ip4.dst == 50.0.0.11 && inport == "DR-S1" &&
> is_chassis_resident("cr-DR-S1") && ct.new),
> action=(ct_commit_to_zone(snat);)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep -e "lr_out_snat" crflows2 | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip &&
> ip4.src == 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.1);)
> >>>> >    table=??(lr_out_snat        ), priority=35   , match=(ip &&
> ip4.src == 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
> >>>> > @@ -1259,6 +1259,7 @@ AT_CAPTURE_FILE([crflows2])
> >>>> >
> >>>> >  AT_CHECK([grep -e "lr_out_snat" drflows3 | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
> ip4.src == 50.0.0.11 && outport == "DR-S1" &&
> is_chassis_resident("cr-DR-S1") && ip4.dst == $allowed_range && (!ct.trk ||
> !ct.rpl)), action=(ct_snat(172.16.1.2);)
> >>>> >  ])
> >>>> > @@ -1269,6 +1270,7 @@ AT_CHECK([grep -e "lr_out_post_snat" drflows3
> | ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep -e "lr_out_snat" crflows3 | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip &&
> ip4.src == 50.0.0.11 && ip4.dst == $allowed_range && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.16.1.2);)
> >>>> >  ])
> >>>> > @@ -1294,6 +1296,7 @@ AT_CAPTURE_FILE([crflows2])
> >>>> >
> >>>> >  AT_CHECK([grep -e "lr_out_snat" drflows4 | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
> ip4.src == 50.0.0.11 && outport == "DR-S1" &&
> is_chassis_resident("cr-DR-S1") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.16.1.2);)
> >>>> >    table=??(lr_out_snat        ), priority=163  , match=(ip &&
> ip4.src == 50.0.0.11 && outport == "DR-S1" &&
> is_chassis_resident("cr-DR-S1") && ip4.dst == $disallowed_range),
> action=(next;)
> >>>> > @@ -1301,6 +1304,7 @@ AT_CHECK([grep -e "lr_out_snat" drflows4 |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep -e "lr_out_snat" crflows4 | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >>>> >    table=??(lr_out_snat        ), priority=33   , match=(ip &&
> ip4.src == 50.0.0.11 && (!ct.trk || !ct.rpl)), action=(ct_snat(172.16.1.2);)
> >>>> >    table=??(lr_out_snat        ), priority=35   , match=(ip &&
> ip4.src == 50.0.0.11 && ip4.dst == $disallowed_range), action=(next;)
> >>>> > @@ -1663,6 +1667,7 @@ AT_CAPTURE_FILE([sbflows])
> >>>> >  # dnat_and_snat or snat entry.
> >>>> >  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip &&
> ip4.dst == 192.168.2.1), action=(ct_snat;)
> >>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip &&
> ip4.dst == 192.168.2.4), action=(ct_snat;)
> >>>> >  ])
> >>>> > @@ -1693,6 +1698,7 @@ AT_CAPTURE_FILE([sbflows])
> >>>> >  # dnat_and_snat or snat entry.
> >>>> >  AT_CHECK([grep "lr_in_unsnat" sbflows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip &&
> ip4.dst == 192.168.2.1), action=(ct_snat;)
> >>>> >    table=??(lr_in_unsnat       ), priority=90   , match=(ip &&
> ip4.dst == 192.168.2.4), action=(ct_snat;)
> >>>> >  ])
> >>>> > @@ -1801,6 +1807,7 @@ ovn-nbctl --wait=sb sync
> >>>> >
> >>>> >  AT_CHECK([ovn-sbctl lflow-list lr0 | grep lr_in_unsnat |
> ovn_strip_lflows], [0], [dnl
> >>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 &&
> ip4.dst == 192.168.2.3), action=(ct_snat;)
> >>>> >  ])
> >>>> >
> >>>> > @@ -4277,12 +4284,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
> >>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 10.0.0.10), action=(ct_dnat;)
> >>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 10.0.0.100), action=(ct_dnat;)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
> ct.new), action=(ct_commit_to_zone(dnat);)
> >>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(ct_lb_mark(backends=10.0.0.4:8080);)
> >>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
> action=(ct_lb_mark(backends=10.0.0.40:8080);)
> >>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> >>>> > @@ -4302,18 +4311,21 @@ AT_CAPTURE_FILE([lr0flows])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip4 &&
> ip4.dst == 20.0.0.4), action=(ct_snat;)
> >>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(ip6 &&
> ip6.dst == aef0::4), action=(ct_snat;)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
> >>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 10.0.0.10), action=(ct_dnat;)
> >>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 10.0.0.100), action=(ct_dnat;)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
> ct.new), action=(ct_commit_to_zone(dnat);)
> >>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
> >>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
> force_snat);)
> >>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> >>>> > @@ -4326,6 +4338,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=100  ,
> match=(flags.force_snat_for_lb == 1 && ip4), action=(ct_snat(20.0.0.4);)
> >>>> >    table=??(lr_out_snat        ), priority=100  ,
> match=(flags.force_snat_for_lb == 1 && ip6), action=(ct_snat(aef0::4);)
> >>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >>>> > @@ -4339,7 +4352,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> >>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> >>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
> ct.new), action=(ct_commit { } ; next; )
> >>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >>>> >  ])
> >>>> >
> >>>> >  check ovn-nbctl --wait=sb set logical_router lr0
> options:lb_force_snat_ip="router_ip"
> >>>> > @@ -4352,6 +4365,7 @@ AT_CHECK([grep "lr_in_ip_input" lr0flows |
> grep "priority=60" | sort], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
> >>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
> >>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
> >>>> > @@ -4359,12 +4373,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
> >>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 10.0.0.10), action=(ct_dnat;)
> >>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 10.0.0.100), action=(ct_dnat;)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
> ct.new), action=(ct_commit_to_zone(dnat);)
> >>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
> >>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
> force_snat);)
> >>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> >>>> > @@ -4377,6 +4393,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
> action=(ct_snat(172.168.0.100);)
> >>>> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
> action=(ct_snat(10.0.0.1);)
> >>>> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
> action=(ct_snat(20.0.0.1);)
> >>>> > @@ -4391,7 +4408,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> >>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> >>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
> ct.new), action=(ct_commit { } ; next; )
> >>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >>>> >  ])
> >>>> >
> >>>> >  check ovn-nbctl --wait=sb remove logical_router lr0 options chassis
> >>>> > @@ -4416,6 +4433,7 @@ AT_CAPTURE_FILE([lr0flows])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
> >>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
> >>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
> >>>> > @@ -4424,12 +4442,14 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
> >>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 10.0.0.10), action=(ct_dnat;)
> >>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 10.0.0.100), action=(ct_dnat;)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
> ct.new), action=(ct_commit_to_zone(dnat);)
> >>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.4:8080;
> force_snat);)
> >>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.100 && tcp && tcp.dst == 80),
> action=(flags.force_snat_for_lb = 1; ct_lb_mark(backends=10.0.0.40:8080;
> force_snat);)
> >>>> >    table=??(lr_in_dnat         ), priority=50   , match=(ct.est &&
> !ct.rel && !ct.new && ct_mark.natted), action=(next;)
> >>>> > @@ -4442,6 +4462,7 @@ AT_CHECK([grep "lr_in_dnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-public"),
> action=(ct_snat(172.168.0.100);)
> >>>> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw0"),
> action=(ct_snat(10.0.0.1);)
> >>>> >    table=??(lr_out_snat        ), priority=110  ,
> match=(flags.force_snat_for_lb == 1 && ip4 && outport == "lr0-sw1"),
> action=(ct_snat(20.0.0.1);)
> >>>> > @@ -4457,7 +4478,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> >>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> >>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
> ct.new), action=(ct_commit { } ; next; )
> >>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >>>> >  ])
> >>>> >
> >>>> >  check ovn-nbctl --wait=sb lb-add lb2 10.0.0.20:80 10.0.0.40:8080
> >>>> > @@ -4468,6 +4489,7 @@ ovn-sbctl dump-flows lr0 > lr0flows
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-public" && ip4.dst == 172.168.0.100), action=(ct_snat;)
> >>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw0" && ip4.dst == 10.0.0.1), action=(ct_snat;)
> >>>> >    table=??(lr_in_unsnat       ), priority=110  , match=(inport ==
> "lr0-sw1" && ip4.dst == 20.0.0.1), action=(ct_snat;)
> >>>> > @@ -4476,6 +4498,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
> >>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 10.0.0.100), action=(ct_dnat;)
> >>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 10.0.0.20), action=(ct_dnat;)
> >>>> >  ])
> >>>> > @@ -4498,7 +4521,7 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> >>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> >>>> > -  table=??(lr_out_post_undnat ), priority=50   , match=(ip &&
> ct.new), action=(ct_commit { } ; next; )
> >>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CLEANUP
> >>>> > @@ -5744,6 +5767,7 @@ AT_CAPTURE_FILE([lr0flows])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
> action=(ct_snat_in_czone;)
> >>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
> flags.use_snat_zone == 1), action=(ct_snat;)
> >>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
> action=(ct_snat_in_czone;)
> >>>> > @@ -5754,10 +5778,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
> ct.new), action=(ct_commit_to_zone(dnat);)
> >>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
> >>>> >  ])
> >>>> >
> >>>> > @@ -5776,10 +5802,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> >>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >>>> >    table=??(lr_out_snat        ), priority=153  , match=(ip &&
> ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat_in_czone(172.168.0.10);)
> >>>> >    table=??(lr_out_snat        ), priority=154  , match=(ip &&
> ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl) && reg9[[4]]
> == 1), action=(reg9[[4]] = 0; ct_snat(172.168.0.10);)
> >>>> > @@ -5799,6 +5827,7 @@ AT_CAPTURE_FILE([lr0flows])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> >>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> >>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.30 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> >>>> > @@ -5806,10 +5835,12 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
> ct.new), action=(ct_commit_to_zone(dnat);)
> >>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_dnat(10.0.0.3);)
> >>>> >  ])
> >>>> >
> >>>> > @@ -5824,24 +5855,20 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> >>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> >>>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip &&
> ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_next(snat);)
> >>>> > -  table=??(lr_out_post_undnat ), priority=70   , match=(ip &&
> ip4.src == 10.0.0.10 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_next(snat);)
> >>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >>>> > -  table=??(lr_out_snat        ), priority=153  , match=(ip &&
> ip4.dst == 10.0.0.0/24 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> >>>> >    table=??(lr_out_snat        ), priority=153  , match=(ip &&
> ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.168.0.10);)
> >>>> > -  table=??(lr_out_snat        ), priority=161  , match=(ip &&
> ip4.dst == 10.0.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_snat;)
> >>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
> ip4.src == 10.0.0.10 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.168.0.30);)
> >>>> >    table=??(lr_out_snat        ), priority=161  , match=(ip &&
> ip4.src == 10.0.0.3 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat(172.168.0.20);)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_post_snat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> >>>> >    table=??(lr_out_post_snat   ), priority=0    , match=(1),
> action=(next;)
> >>>> > -  table=??(lr_out_post_snat   ), priority=153  , match=(ip &&
> ip4.dst == 10.0.0.0/24 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && ct.new),
> action=(ct_commit_to_zone(snat);)
> >>>> > -  table=??(lr_out_post_snat   ), priority=161  , match=(ip &&
> ip4.dst == 10.0.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && ct.new),
> action=(ct_commit_to_zone(snat);)
> >>>> >  ])
> >>>> >
> >>>> >  # Associate load balancer to lr0
> >>>> > @@ -5866,6 +5893,7 @@ AT_CAPTURE_FILE([lr0flows])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_unsnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_unsnat       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_unsnat       ), priority=10   , match=(ip),
> action=(flags.without_unsnat = 1; next;)
> >>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
> action=(ct_snat_in_czone;)
> >>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.10 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 1 &&
> flags.use_snat_zone == 1), action=(ct_snat;)
> >>>> >    table=??(lr_in_unsnat       ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && flags.loopback == 0),
> action=(ct_snat_in_czone;)
> >>>> > @@ -5876,6 +5904,7 @@ AT_CHECK([grep "lr_in_unsnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_defrag       ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl)), action=(ct_next(dnat);)
> >>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 10.0.0.10), action=(ct_dnat;)
> >>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.100), action=(ct_dnat;)
> >>>> >    table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.200), action=(ct_dnat;)
> >>>> > @@ -5884,6 +5913,7 @@ AT_CHECK([grep "lr_in_defrag" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0], [dnl
> >>>> >    table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_in_dnat         ), priority=10   , match=(ip &&
> ct.new), action=(ct_commit_to_zone(dnat);)
> >>>> >    table=??(lr_in_dnat         ), priority=100  , match=(ip &&
> ip4.dst == 172.168.0.20 && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_dnat_in_czone(10.0.0.3);)
> >>>> >    table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 172.168.0.200 &&
> is_chassis_resident("cr-lr0-public")),
> action=(ct_lb_mark(backends=10.0.0.80,10.0.0.81);)
> >>>> >    table=??(lr_in_dnat         ), priority=120  , match=(ct.new &&
> !ct.rel && ip4 && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 &&
> is_chassis_resident("cr-lr0-public")),
> action=(ct_lb_mark(backends=10.0.0.4:8080);)
> >>>> > @@ -5916,10 +5946,12 @@ AT_CHECK([grep "lr_out_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_post_undnat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> >>>> >    table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_post_undnat ), priority=10   , match=(ip &&
> (!ct.trk || !ct.rpl) && flags.without_unsnat == 1), action=(ct_next(snat);)
> >>>> >  ])
> >>>> >
> >>>> >  AT_CHECK([grep "lr_out_snat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> >>>> >    table=??(lr_out_snat        ), priority=0    , match=(1),
> action=(next;)
> >>>> > +  table=??(lr_out_snat        ), priority=10   , match=(ip &&
> ct.new && flags.without_unsnat == 1), action=(ct_commit_to_zone(snat);)
> >>>> >    table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> >>>> >    table=??(lr_out_snat        ), priority=153  , match=(ip &&
> ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public") && (!ct.trk || !ct.rpl)),
> action=(ct_snat_in_czone(172.168.0.10);)
> >>>> >    table=??(lr_out_snat        ), priority=154  , match=(ip &&
> ip4.src == 10.0.0.0/24 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-pub