On Wed, Feb 12, 2025 at 6:46 PM Han Zhou <[email protected]> wrote:

>
>
> On Wed, Feb 12, 2025 at 9:36 AM Han Zhou <[email protected]> wrote:
> >
> >
> >
> > On Tue, Feb 11, 2025 at 11:04 PM Ales Musil <[email protected]> wrote:
> > >
> > >
> > >
> > > On Wed, Feb 12, 2025 at 7:52 AM Han Zhou <[email protected]> wrote:
> > >>
> > >> Hi Ales, Mark,
> > >>
> > >> I have some more comments below.
> > >
> > >
> > > Hi Han,
> > >
> > > thank you for the review, please find my response below.
> > >
> > >>
> > >> On Tue, Feb 11, 2025 at 11:58 AM Mark Michelson <[email protected]>
> wrote:
> > >> >
> > >> > Hi Ales,
> > >> >
> > >> > I have some notes below
> > >> >
> > >> > On 2/5/25 07:58, Ales Musil wrote:
> > >> > > Add LR option to commit all traffic that is not already commit by
> > >> >
> > >> > s/already commit/already committed/
> > >> >
> > >> > > 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.
> > >> > >
> > >> > > Reported-at: https://issues.redhat.com/browse/FDP-787
> > >> > > Signed-off-by: Ales Musil <[email protected]>
> > >> > > ---
> > >> > > v4: Rebase on top of latest main.
> > >> > >      Adjust the option name.
> > >> > >      Adjust the NEWS entry.
> > >> > >      Update ovn-northd.8.xml.
> > >> > >      Change the option so it affects both zones regardless if
> there is only stateful SNAT or DNAT.
> > >> > >      Add comment about !ct.rpl optimization.
> > >> > >
> > >> > > v3: Rebase on top of latest main.
> > >> > >      Add extra system test that checks specific scenario which
> was broken without this option.
> > >> > > ---
> > >> > >   NEWS                         |   2 +
> > >> > >   include/ovn/logical-fields.h |   4 +
> > >> > >   lib/logical-fields.c         |   4 +
> > >> > >   northd/northd.c              | 132 ++++++++-
> > >> > >   northd/northd.h              |  39 +--
> > >> > >   northd/ovn-northd.8.xml      | 111 +++++--
> > >> > >   ovn-nb.xml                   |   8 +
> > >> > >   tests/ovn-northd.at          | 256 ++++++++++++++++
> > >> > >   tests/system-ovn-kmod.at     | 556
> +++++++++++++++++++++++++++++++++++
> > >> > >   9 files changed, 1067 insertions(+), 45 deletions(-)
> > >> > >
> > >> > > diff --git a/NEWS b/NEWS
> > >> > > index f9da7bae1..cd324347f 100644
> > >> > > --- a/NEWS
> > >> > > +++ b/NEWS
> > >> > > @@ -41,6 +41,8 @@ Post v24.09.0
> > >> > >      - Add concept of Transit Routers, users are now allowed to
> specify
> > >> > >        options:requested-chassis for router ports; if the chassis
> is remote
> > >> > >        then the router port will behave as a remote port.
> > >> > > +   - Add "options:ct-commit-all" to LR, that enables commit of
> all traffic
> > >> > > +     to DNAT and SNAT zone when LR is stateful.
> > >> > >
> > >> > >   OVN v24.09.0 - 13 Sep 2024
> > >> > >   --------------------------
> > >> > > diff --git a/include/ovn/logical-fields.h
> b/include/ovn/logical-fields.h
> > >> > > index f853b1f61..175e5e7d4 100644
> > >> > > --- a/include/ovn/logical-fields.h
> > >> > > +++ b/include/ovn/logical-fields.h
> > >> > > @@ -95,6 +95,7 @@ enum mff_log_flags_bits {
> > >> > >       MLF_ICMP_SNAT_BIT = 17,
> > >> > >       MLF_OVERRIDE_LOCAL_ONLY_BIT = 18,
> > >> > >       MLF_FROM_CTRL_BIT = 19,
> > >> > > +    MLF_UNSNAT_NEW_BIT = 20,
> > >> > >   };
> > >> > >
> > >> > >   /* MFF_LOG_FLAGS_REG flag assignments */
> > >> > > @@ -152,6 +153,9 @@ enum mff_log_flags {
> > >> > >       MLF_ICMP_SNAT = (1 << MLF_ICMP_SNAT_BIT),
> > >> > >
> > >> > >       MLF_OVERRIDE_LOCAL_ONLY = (1 <<
> MLF_OVERRIDE_LOCAL_ONLY_BIT),
> > >> > > +
> > >> > > +    /* Indicate that the packet didn't go through unSNAT. */
> > >> > > +    MLF_UNSNAT_NEW = (1 << MLF_UNSNAT_NEW_BIT),
> > >> > >   };
> > >> > >
> > >> > >   /* OVN logical fields
> > >> > > diff --git a/lib/logical-fields.c b/lib/logical-fields.c
> > >> > > index f49a0a79d..1026a8c9e 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_UNSNAT_NEW_BIT);
> > >> > > +    expr_symtab_add_subfield(symtab, "flags.unsnat_new", NULL,
> > >> > > +                             flags_str);
> > >> > >
> > >> > >       snprintf(flags_str, sizeof flags_str, "flags[%d]",
> MLF_FROM_CTRL_BIT);
> > >> > >       expr_symtab_add_subfield(symtab, "flags.from_ctrl", NULL,
> flags_str);
> > >> > > diff --git a/northd/northd.c b/northd/northd.c
> > >> > > index 587fcd586..72552e127 100644
> > >> > > --- a/northd/northd.c
> > >> > > +++ b/northd/northd.c
> > >> > > @@ -15987,7 +15987,8 @@ build_lrouter_out_snat_flow(struct
> lflow_table *lflows,
> > >> > >        * 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) {
> > >> > > +        nat_entry->type == SNAT && !od->is_gw_router &&
> > >> > > +        !smap_get_bool(&od->nbr->options, "ct-commit-all",
> false)) {
> > >> > >           /* For traffic that comes from SNAT network, initiate
> CT state before
> > >> > >            * entering S_ROUTER_OUT_SNAT to allow matching on
> various CT states.
> > >> > >            */
> > >> > > @@ -16291,6 +16292,8 @@ static void
> build_lr_nat_defrag_and_lb_default_flows(
> > >> > >       /* Packets are allowed by default. */
> > >> > >       ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 0, "1",
> "next;", lflow_ref);
> > >> > >       ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 0, "1",
> "next;", lflow_ref);
> > >> > > +    ovn_lflow_add(lflows, od, S_ROUTER_IN_POST_UNSNAT, 0, "1",
> "next;",
> > >> > > +                  lflow_ref);
> > >> > >       ovn_lflow_add(lflows, od, S_ROUTER_OUT_CHECK_DNAT_LOCAL, 0,
> "1",
> > >> > >                     REGBIT_DST_NAT_IP_LOCAL" = 0; next;",
> lflow_ref);
> > >> > >       ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 0, "1",
> "next;", lflow_ref);
> > >> > > @@ -16319,6 +16322,106 @@ static void
> build_lr_nat_defrag_and_lb_default_flows(
> > >> > >                     lflow_ref);
> > >> > >   }
> > >> > >
> > >> > > +static void
> > >> > > +build_gw_lrouter_commit_all(const struct ovn_datapath *od,
> > >> > > +                            struct lflow_table *lflows,
> > >> > > +                            const struct chassis_features
> *features,
> > >> > > +                            struct lflow_ref *lflow_ref)
> > >> > > +{
> > >> > > +    ovs_assert(od->is_gw_router);
> > >> > > +    if (!(features->ct_commit_to_zone &&
> features->ct_next_zone)) {
> > >> > > +        return;
> > >> > > +    }
> > >> > > +
> > >> > > +    /* Note: We can use match on "!ct.rpl" as optimization here,
> even if the
> > >> > > +     * previous state is from different zone. The packet that is
> already reply
> > >> > > +     * should be reply in both zones. */
> > >> > > +    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);
> > >> > > +
> > >> > > +    /* We would lose the CT state especially the ct.new flag if
> we have
> > >> > > +     * mixed SNAT and DNAT on single LR. In order to know if we
> actually
> > >> > > +     * can commit into SNAT zone keep the flag in register. The
> SNAT flows
> > >> > > +     * in the egress pipeline can then check the flag and commit
> > >> > > +     * based on that. */
> > >> > > +    ovn_lflow_add(lflows, od, S_ROUTER_IN_POST_UNSNAT, 10,
> > >> > > +                  "ip && (!ct.trk || ct.new)",
> > >> > > +                  "flags.unsnat_new = 1; next;", lflow_ref);
> > >>
> > >> According to the above logic, the unsnat_new flag can mean two
> things, either the packet didn't go through SNAT zone during unsnat stage,
> or it went through and came back with "ct.new" state. For the latter case,
> it is unnecessary to go through SNAT zone again in egress pipeline, but in
> the below logic it still performs ct_next(snat) in this case, which would
> be a waste. So would it be better to separate the two cases, probably with
> flag.unsnat_no_track and flag.unsnat_new? Please see my comments below.
> > >
> > >
> > > If I understand it correctly you would like to add further
> > > optimization at the cost of another flag bit, is that correct?
> >
> > Correct
> >
> > > I'm not sure I see a huge benefit with this optimization, the reason
> > > being that the pattern for traffic that didn't hit usnat won't
> > > change, it will change for traffic that did hit unsnat but wasn't
> > > committed yet. Which should be a small portion of the overall traffic
> > > would you agree? Once it has any other state than new it will be
> > > skipped in the egress pipeline regardless.
> > >
> >
> > I think it may be a big portion of the traffic if we consider the very
> typical scenario in ovn-k8s, the node-port LB, which uses the node IP as
> the GR's uplink IP and LB IP, so all the node-port connection initiation
> traffic will traverse the SNAT zone at the UNSNAT stage and return as
> ct.new state.
>
> I should clarify that the portion of the traffic really depends on the
> traffic pattern. By "a big portion of the traffic" I meant for the
> scenarios when most traffic is short-lived connections, e.g. clients making
> frequent API calls, because these traffic frequently setup TCP connections
> and close them immediately. On the other hand, for long-lived connections I
> agree the portion of the traffic hitting unsnat with ct.new should be very
> small.
>

Hi Han,
short lived connection to node service makes sense and for them we would
indeed have overhead.

>
> >
> > Alternatively, we can avoid the extra flag by just committing to SNAT
> directly in S_ROUTER_IN_POST_UNSNAT, and only set unsnat_no_track = 1 for
> "!ct.trk". This way we don't need the flags.unsnat_new because we already
> committed it if it was ct.new.
>
>
> > What do you think?
>

I am trying to think if there would possibly be a scenario when we would
commit in POST_UNSNAT and then commit again in SNAT, but that would be a
very strange topology to SNAT towards the internal network. I think we can
go with a commit in POST_UNSNAT as we shouldn't care that this tuple is a
pre-DNAT one, we don't need to keep the order of operation for this. I will
happily avoid using another flag on this (we are running out of them
quickly). I will adjust the approach in v5.


> >
> > Thanks,
> > Han
> >
>

Thanks,
Ales

> >>
> > >> > > +    /* Note: We can use match on "!ct.rpl" as optimization here,
> even if the
> > >> > > +     * previous state is from different zone. The packet that is
> already reply
> > >> > > +     * should be reply in both zones. */
> > >> > > +    ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 10,
> > >> > > +                  "ip && (!ct.trk || !ct.rpl) && "
> > >> > > +                  "flags.unsnat_new == 1", "ct_next(snat);",
> > >> > > +                  lflow_ref);
> > >>
> > >> flags.unsnat_no_track == 1, ct_next(snat)
> > >> flags.unsnat_new == 1, next (skip CT)
> > >>
> > >> > > +    ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 10,
> > >> > > +                  "ip && ct.new && flags.unsnat_new == 1",
> > >> > > +                  "ct_commit_to_zone(snat);", lflow_ref);
> > >>
> > >> ct.new || flags.unsnat_new == 1, ct_commit_to_zone(snat)
> > >>
> > >> P.S. Alin is doing HW offload performance tests for this patch and I
> am also doing some more tests.
> > >
> > >
> > >
> > > Looking forward to see the results.
> > >
> > >>
> > >> Thanks,
> > >> Han
> > >>
> > >>
> > >
> > > Thanks,
> > > Ales
> > >
> > >>
> > >> > > +}
> > >> > > +
> > >> > > +static void
> > >> > > +build_dgp_lrouter_commit_all(const struct ovn_datapath *od,
> > >> > > +                             const struct ovn_port *l3dgw_port,
> > >> > > +                             struct lflow_table *lflows,
> > >> > > +                             const struct chassis_features
> *features,
> > >> > > +                             struct ds *match, struct lflow_ref
> *lflow_ref)
> > >> > > +{
> > >> > > +    ovs_assert(od->n_l3dgw_ports);
> > >> > > +    if (!(features->ct_commit_to_zone &&
> features->ct_next_zone)) {
> > >> > > +        return;
> > >> > > +    }
> > >> > > +
> > >> > > +    /* Note: We can use match on "!ct.rpl" as optimization here,
> even if the
> > >> > > +     * previous state is from different zone. The packet that is
> already reply
> > >> > > +     * should be reply in both zones. */
> > >> > > +    ds_clear(match);
> > >> > > +    ds_put_format(match, "ip && (!ct.trk || !ct.rpl) && "
> > >> > > +                  "inport == %s && is_chassis_resident(%s)",
> > >> > > +                  l3dgw_port->json_key,
> l3dgw_port->cr_port->json_key);
> > >> > > +    ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 10,
> ds_cstr(match),
> > >> > > +                  "ct_next(dnat);", lflow_ref);
> > >> > > +
> > >> > > +    ds_clear(match);
> > >> > > +    ds_put_format(match, "ip && ct.new && inport == %s && "
> > >> > > +                 "is_chassis_resident(%s)", l3dgw_port->json_key,
> > >> > > +                 l3dgw_port->cr_port->json_key);
> > >> > > +    ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 10,
> ds_cstr(match),
> > >> > > +                  "ct_commit_to_zone(dnat);", lflow_ref);
> > >> > > +
> > >> > > +    /* We would lose the CT state especially the ct.new flag if
> we have
> > >> > > +     * mixed SNAT and DNAT on single LR. In order to know if we
> actually
> > >> > > +     * can commit into SNAT zone keep the flag in register. The
> SNAT flows
> > >> > > +     * in the egress pipeline can then check the flag and commit
> > >> > > +     * based on that. */
> > >> > > +    ds_clear(match);
> > >> > > +    ds_put_format(match, "ip && (!ct.trk || ct.new) && "
> > >> > > +                         "inport == %s &&
> is_chassis_resident(%s)",
> > >> > > +                  l3dgw_port->json_key,
> l3dgw_port->cr_port->json_key);
> > >> > > +    ovn_lflow_add(lflows, od, S_ROUTER_IN_POST_UNSNAT, 10,
> ds_cstr(match),
> > >> > > +                  "flags.unsnat_new = 1; next;", lflow_ref);
> > >> > > +
> > >> > > +    /* Note: We can use match on "!ct.rpl" as optimization here,
> even if the
> > >> > > +     * previous state is from different zone. The packet that is
> already reply
> > >> > > +     * should be reply in both zones. */
> > >> > > +    ds_clear(match);
> > >> > > +    ds_put_format(match, "ip && (!ct.trk || !ct.rpl) && "
> > >> > > +                 "flags.unsnat_new == 1 && outport == %s && "
> > >> > > +                 "is_chassis_resident(%s)", l3dgw_port->json_key,
> > >> > > +                 l3dgw_port->cr_port->json_key);
> > >> > > +    ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT, 10,
> > >> > > +                  ds_cstr(match), "ct_next(snat);", lflow_ref);
> > >> > > +
> > >> > > +    ds_clear(match);
> > >> > > +    ds_put_format(match, "ip && ct.new && flags.unsnat_new == 1
> && "
> > >> > > +                  "outport == %s && is_chassis_resident(%s)",
> > >> > > +                  l3dgw_port->json_key,
> l3dgw_port->cr_port->json_key);
> > >> > > +    ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 10,
> ds_cstr(match),
> > >> > > +                  "ct_commit_to_zone(snat);", lflow_ref);
> > >> > > +}
> > >> > > +
> > >> > >   static void
> > >> > >   build_lrouter_nat_defrag_and_lb(
> > >> > >       const struct lr_stateful_record *lr_stateful_rec,
> > >> > > @@ -16329,6 +16432,8 @@ build_lrouter_nat_defrag_and_lb(
> > >> > >       const struct chassis_features *features,
> > >> > >       struct lflow_ref *lflow_ref)
> > >> > >   {
> > >> > > +
> > >> > > +    bool commit_all = smap_get_bool(&od->nbr->options,
> "ct-commit-all", false);
> > >> > >       /* Ingress DNAT (Priority 50/70).
> > >> > >        *
> > >> > >        * Allow traffic that is related to an existing conntrack
> entry.
> > >> > > @@ -16403,9 +16508,11 @@ 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);
> > >> > > +        if (!commit_all) {
> > >> > > +            ovn_lflow_add(lflows, od, S_ROUTER_OUT_POST_UNDNAT,
> 50,
> > >> > > +                          "ip && ct.new", "ct_commit { } ; next;
> ",
> > >> > > +                          lflow_ref);
> > >> > > +        }
> > >> >
> > >> > This ct_commit flow is not added if commit_all is not set. My
> assumption
> > >> > is that this flow is supposed to be replaced by the flows installed
> in
> > >> > build_gw_lrouter_commit_all() and/or build_dgp_lrouter_commit_all().
> > >> > What happens if the "stateful" variable that is declared later in
> this
> > >> > function is not true? Then we don't install the commit_all flows,
> and so
> > >> > this ct_commit flow is simply missing. On the one hand, it seems
> like an
> > >> > oversight because there's a case where we aren't calling ct_commit()
> > >> > where we used to. However, if there truly are no stateful NAT rules,
> > >> > then was it actually a mistake for us to be calling ct_commit()
> here in
> > >> > the first place?
> > >> >
> > >> > >       }
> > >> > >
> > >> > >       /* NAT rules are only valid on Gateway routers and routers
> with
> > >> > > @@ -16423,7 +16530,8 @@ 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 = (lr_stateful_rec->has_lb_vip ||
> 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];
> > >> > >           const struct nbrec_nat *nat = nat_entry->nb;
> > >> > > @@ -16441,6 +16549,8 @@ build_lrouter_nat_defrag_and_lb(
> > >> > >               continue;
> > >> > >           }
> > >> > >
> > >> > > +        stateful |= !stateless;
> > >> > > +
> > >> > >           /* S_ROUTER_IN_UNSNAT
> > >> > >            * Ingress UNSNAT table: It is for already established
> connections'
> > >> > >            * reverse traffic. i.e., SNAT has already been done in
> egress
> > >> > > @@ -16653,6 +16763,18 @@ build_lrouter_nat_defrag_and_lb(
> > >> > >           }
> > >> > >       }
> > >> > >
> > >> > > +    if (commit_all && stateful) {
> > >> > > +        if (od->is_gw_router) {
> > >> > > +            build_gw_lrouter_commit_all(od, lflows, features,
> lflow_ref);
> > >> > > +        }
> > >> > > +
> > >> > > +        for (size_t i = 0; i < od->n_l3dgw_ports; i++) {
> > >> > > +            struct ovn_port *l3dgw_port = od->l3dgw_ports[i];
> > >> > > +            build_dgp_lrouter_commit_all(od, l3dgw_port, lflows,
> > >> > > +                                         features, match,
> 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/northd/northd.h b/northd/northd.h
> > >> > > index 60bfd4f8f..52c047c16 100644
> > >> > > --- a/northd/northd.h
> > >> > > +++ b/northd/northd.h
> > >> > > @@ -478,27 +478,28 @@ enum ovn_stage {
> > >> > >       PIPELINE_STAGE(ROUTER, IN,  IP_INPUT,        3,
> "lr_in_ip_input")     \
> > >> > >       PIPELINE_STAGE(ROUTER, IN,  DHCP_RELAY_REQ,  4,
> "lr_in_dhcp_relay_req") \
> > >> > >       PIPELINE_STAGE(ROUTER, IN,  UNSNAT,          5,
> "lr_in_unsnat")       \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  DEFRAG,          6,
> "lr_in_defrag")       \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  LB_AFF_CHECK,    7,
> "lr_in_lb_aff_check") \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  DNAT,            8,
> "lr_in_dnat")         \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  LB_AFF_LEARN,    9,
> "lr_in_lb_aff_learn") \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  ECMP_STATEFUL,   10,
> "lr_in_ecmp_stateful") \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  ND_RA_OPTIONS,   11,
> "lr_in_nd_ra_options") \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  ND_RA_RESPONSE,  12,
> "lr_in_nd_ra_response") \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING_PRE,  13,
> "lr_in_ip_routing_pre")  \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING,      14,
> "lr_in_ip_routing")      \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING_ECMP, 15,
> "lr_in_ip_routing_ecmp") \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  POLICY,          16,
> "lr_in_policy")          \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  POLICY_ECMP,     17,
> "lr_in_policy_ecmp")     \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  DHCP_RELAY_RESP_CHK, 18,
>              \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  POST_UNSNAT,     6,
> "lr_in_post_unsnat")  \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  DEFRAG,          7,
> "lr_in_defrag")       \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  LB_AFF_CHECK,    8,
> "lr_in_lb_aff_check") \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  DNAT,            9,
> "lr_in_dnat")         \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  LB_AFF_LEARN,    10,
> "lr_in_lb_aff_learn") \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  ECMP_STATEFUL,   11,
> "lr_in_ecmp_stateful") \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  ND_RA_OPTIONS,   12,
> "lr_in_nd_ra_options") \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  ND_RA_RESPONSE,  13,
> "lr_in_nd_ra_response") \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING_PRE,  14,
> "lr_in_ip_routing_pre")  \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING,      15,
> "lr_in_ip_routing")      \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING_ECMP, 16,
> "lr_in_ip_routing_ecmp") \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  POLICY,          17,
> "lr_in_policy")          \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  POLICY_ECMP,     18,
> "lr_in_policy_ecmp")     \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  DHCP_RELAY_RESP_CHK, 19,
>              \
> > >> > >                     "lr_in_dhcp_relay_resp_chk")
>                \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  DHCP_RELAY_RESP, 19,
>              \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  DHCP_RELAY_RESP, 20,
>              \
> > >> > >                     "lr_in_dhcp_relay_resp")
>                \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  ARP_RESOLVE,     20,
> "lr_in_arp_resolve")     \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  CHK_PKT_LEN,     21,
> "lr_in_chk_pkt_len")     \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  LARGER_PKTS,     22,
> "lr_in_larger_pkts")     \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  GW_REDIRECT,     23,
> "lr_in_gw_redirect")     \
> > >> > > -    PIPELINE_STAGE(ROUTER, IN,  ARP_REQUEST,     24,
> "lr_in_arp_request")     \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  ARP_RESOLVE,     21,
> "lr_in_arp_resolve")     \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  CHK_PKT_LEN,     22,
> "lr_in_chk_pkt_len")     \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  LARGER_PKTS,     23,
> "lr_in_larger_pkts")     \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  GW_REDIRECT,     24,
> "lr_in_gw_redirect")     \
> > >> > > +    PIPELINE_STAGE(ROUTER, IN,  ARP_REQUEST,     25,
> "lr_in_arp_request")     \
> > >> > >
>       \
> > >> > >       /* Logical router egress stages. */
>       \
> > >> > >       PIPELINE_STAGE(ROUTER, OUT, CHECK_DNAT_LOCAL,   0,
>               \
> > >> > > diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
> > >> > > index 93b1a9135..6f50a851e 100644
> > >> > > --- a/northd/ovn-northd.8.xml
> > >> > > +++ b/northd/ovn-northd.8.xml
> > >> > > @@ -3667,7 +3667,24 @@ next;
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 6: DEFRAG</h3>
> > >> > > +    <h3>Ingress Table 6: POST USNAT</h3>
> > >> > > +
> > >> > > +    <p>
> > >> > > +      This is to check whether the packet is already tracked in
> SNAT zone.
> > >> > > +      It contains a priority-0 flow that simply moves traffic to
> the next
> > >> > > +      table.
> > >> > > +    </p>
> > >> > > +
> > >> > > +    <p>
> > >> > > +      If the <code>optins:ct-commit-all</code> is set to
> <code>true</code> the
> > >> >
> > >> > s/optins/options/
> > >> >
> > >> > This mistake is made every time the option is referenced in this
> file. I
> > >> > think it occurs 7 times in total.
> > >> >
> > >> > > +      following flow is configured matching on <code>ip
> &amp;&amp;
> > >> > > +      (!ct.trk || ct.new)</code> with an action
> <code>flags.unsnat_new = 1;
> > >> > > +      next; </code> which sets a flag that is used in later
> stages.
> > >> > > +      There is extra match when there is configured DGP
> > >> > > +      <code>inport == DGP &amp;&amp;
> is_chassis_resident(CHASSIS)</code>.
> > >> > > +    </p>
> > >> > > +
> > >> > > +    <h3>Ingress Table 7: DEFRAG</h3>
> > >> > >
> > >> > >       <p>
> > >> > >         This is to send packets to connection tracker for
> tracking and
> > >> > > @@ -3710,7 +3727,15 @@ next;
> > >> > >         this allows potentially related ICMP traffic to pass
> through CT.
> > >> > >       </p>
> > >> > >
> > >> > > -    <h3>Ingress Table 7: Load balancing affinity check</h3>
> > >> > > +    <p>
> > >> > > +      If the <code>optins:ct-commit-all</code> is set to
> <code>true</code>
> > >> > > +      the following flow is configured matching on <code>ip
> &amp;&amp;
> > >> > > +      (!ct.trk || !ct.rpl)</code> with an action
> <code>ct_next(dnat);</code>.
> > >> > > +      There is extra match when the LR is configured as DGP
> > >> > > +      <code>inport == DGP &amp;&amp;
> is_chassis_resident(CHASSIS)</code>.
> > >> > > +    </p>
> > >> > > +
> > >> > > +   <h3>Ingress Table 8: Load balancing affinity check</h3>
> > >> > >
> > >> > >       <p>
> > >> > >         Load balancing affinity check table contains the following
> > >> > > @@ -3737,7 +3762,7 @@ next;
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 8: DNAT</h3>
> > >> > > +    <h3>Ingress Table 9: DNAT</h3>
> > >> > >
> > >> > >       <p>
> > >> > >         Packets enter the pipeline with destination IP address
> that needs to
> > >> > > @@ -3866,7 +3891,7 @@ next;
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <p>Ingress Table 8: DNAT on Gateway Routers</p>
> > >> > > +    <p>Ingress Table 9: DNAT on Gateway Routers</p>
> > >> > >
> > >> > >       <ul>
> > >> > >         <li>
> > >> > > @@ -3919,13 +3944,21 @@ next;
> > >> > >           </p>
> > >> > >         </li>
> > >> > >
> > >> > > +      <li>
> > >> > > +        <p>
> > >> > > +          If the <code>optins:ct-commit-all</code> is set to
> <code>true</code>
> > >> > > +          the following flow is configured matching on <code>ip
> &amp;&amp;
> > >> > > +          ct.new</code> with an action
> <code>ct_commit_to_zone(dnat);</code>.
> > >> > > +        </p>
> > >> > > +      </li>
> > >> > > +
> > >> > >         <li>
> > >> > >           A priority-0 logical flow with match <code>1</code> has
> actions
> > >> > >           <code>next;</code>.
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <p>Ingress Table 8: DNAT on Distributed Routers</p>
> > >> > > +    <p>Ingress Table 9: DNAT on Distributed Routers</p>
> > >> > >
> > >> > >       <p>
> > >> > >         On distributed routers, the DNAT table only handles
> packets
> > >> > > @@ -3973,6 +4006,14 @@ next;
> > >> > >             <code>exempted_ext_ips</code>.
> > >> > >           </p>
> > >> > >
> > >> > > +        <p>
> > >> > > +          If the <code>optins:ct-commit-all</code> is set to
> <code>true</code>
> > >> > > +          the following flow is configured matching on <code>ip
> &amp;&amp;
> > >> > > +          ct.new &amp;&amp; inport == DGP &amp;&amp;
> > >> > > +          is_chassis_resident(CHASSIS)</code> with an action
> > >> > > +          <code>ct_commit_to_zone(dnat);</code>.
> > >> > > +        </p>
> > >> > > +
> > >> > >           <p>
> > >> > >             A priority-0 logical flow with match <code>1</code>
> has actions
> > >> > >             <code>next;</code>.
> > >> > > @@ -3980,7 +4021,7 @@ next;
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 9: Load balancing affinity learn</h3>
> > >> > > +   <h3>Ingress Table 10: Load balancing affinity learn</h3>
> > >> > >
> > >> > >       <p>
> > >> > >         Load balancing affinity learn table contains the following
> > >> > > @@ -4008,7 +4049,7 @@ next;
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 10: ECMP symmetric reply processing</h3>
> > >> > > +   <h3>Ingress Table 11: ECMP symmetric reply processing</h3>
> > >> > >       <ul>
> > >> > >         <li>
> > >> > >           If ECMP routes with symmetric reply are configured in
> the
> > >> > > @@ -4027,7 +4068,7 @@ next;
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 11: IPv6 ND RA option processing</h3>
> > >> > > +   <h3>Ingress Table 12: IPv6 ND RA option processing</h3>
> > >> > >
> > >> > >       <ul>
> > >> > >         <li>
> > >> > > @@ -4057,7 +4098,7 @@ reg0[5] =
> put_nd_ra_opts(<var>options</var>);next;
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 12: IPv6 ND RA responder</h3>
> > >> > > +    <h3>Ingress Table 13: IPv6 ND RA responder</h3>
> > >> > >
> > >> > >       <p>
> > >> > >         This table implements IPv6 ND RA responder for the IPv6
> ND RA replies
> > >> > > @@ -4102,7 +4143,7 @@ output;
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 13: IP Routing Pre</h3>
> > >> > > +   <h3>Ingress Table 14: IP Routing Pre</h3>
> > >> > >
> > >> > >       <p>
> > >> > >         If a packet arrived at this table from Logical Router
> Port <var>P</var>
> > >> > > @@ -4132,7 +4173,7 @@ output;
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 14: IP Routing</h3>
> > >> > > +   <h3>Ingress Table 15: IP Routing</h3>
> > >> > >
> > >> > >       <p>
> > >> > >         A packet that arrives at this table is an IP packet that
> should be
> > >> > > @@ -4349,7 +4390,7 @@ reg8[16..31] = <var>MID1</var>);
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 15: IP_ROUTING_ECMP</h3>
> > >> > > +    <h3>Ingress Table 16: IP_ROUTING_ECMP</h3>
> > >> > >
> > >> > >       <p>
> > >> > >         This table implements the second part of IP routing for
> ECMP routes
> > >> > > @@ -4406,7 +4447,7 @@ outport = <var>P</var>;
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 16: Router policies</h3>
> > >> > > +   <h3>Ingress Table 17: Router policies</h3>
> > >> > >       <p>
> > >> > >         This table adds flows for the logical router policies
> configured
> > >> > >         on the logical router. Please see the
> > >> > > @@ -4478,7 +4519,7 @@ next;
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 17: ECMP handling for router policies</h3>
> > >> > > +    <h3>Ingress Table 18: ECMP handling for router policies</h3>
> > >> > >       <p>
> > >> > >         This table handles the ECMP for the router policies
> configured
> > >> > >         with multiple nexthops.
> > >> > > @@ -4527,7 +4568,7 @@ outport = <var>P</var>
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 18: DHCP Relay Response Check</h3>
> > >> > > +    <h3>Ingress Table 19: DHCP Relay Response Check</h3>
> > >> > >       <p>
> > >> > >         This stage process the DHCP response packets coming from
> the DHCP server.
> > >> > >       </p>
> > >> > > @@ -4561,7 +4602,7 @@ outport = <var>P</var>
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 19: DHCP Relay Response</h3>
> > >> > > +    <h3>Ingress Table 20: DHCP Relay Response</h3>
> > >> > >       <p>
> > >> > >         This stage process the DHCP response packets on which
> > >> > >         <code>dhcp_relay_resp_chk</code> action is applied in the
> previous stage.
> > >> > > @@ -4604,7 +4645,7 @@ output;
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 20: ARP/ND Resolution</h3>
> > >> > > +   <h3>Ingress Table 21: ARP/ND Resolution</h3>
> > >> > >
> > >> > >       <p>
> > >> > >         Any packet that reaches this table is an IP packet whose
> next-hop
> > >> > > @@ -4818,7 +4859,7 @@ output;
> > >> > >
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 21: Check packet length</h3>
> > >> > > +   <h3>Ingress Table 22: Check packet length</h3>
> > >> > >
> > >> > >       <p>
> > >> > >         For distributed logical routers or gateway routers with
> gateway
> > >> > > @@ -4855,7 +4896,7 @@ REGBIT_PKT_LARGER =
> check_pkt_larger(<var>L</var>); next;
> > >> > >         and advances to the next table.
> > >> > >       </p>
> > >> > >
> > >> > > -    <h3>Ingress Table 22: Handle larger packets</h3>
> > >> > > +   <h3>Ingress Table 23: Handle larger packets</h3>
> > >> > >
> > >> > >       <p>
> > >> > >         For distributed logical routers or gateway routers with
> gateway port
> > >> > > @@ -4918,7 +4959,7 @@ icmp6 {
> > >> > >         and advances to the next table.
> > >> > >       </p>
> > >> > >
> > >> > > -    <h3>Ingress Table 23: Gateway Redirect</h3>
> > >> > > +   <h3>Ingress Table 24: Gateway Redirect</h3>
> > >> > >
> > >> > >       <p>
> > >> > >         For distributed logical routers where one or more of the
> logical router
> > >> > > @@ -5002,7 +5043,7 @@ icmp6 {
> > >> > >         </li>
> > >> > >       </ul>
> > >> > >
> > >> > > -    <h3>Ingress Table 24: ARP Request</h3>
> > >> > > +   <h3>Ingress Table 25: ARP Request</h3>
> > >> > >
> > >> > >       <p>
> > >> > >         In the common case where the Ethernet destination has
> been resolved, this
> > >> > > @@ -5202,6 +5243,15 @@ nd_ns {
> > >> > >             with action <code>ct_commit { } ; next; </code>.
> > >> > >           </li>
> > >> > >
> > >> > > +        <li>
> > >> > > +          If the <code>optins:ct-commit-all</code> is set to
> <code>true</code>
> > >> > > +          the following flow is configured matching on <code>ip
> &amp;&amp;
> > >> > > +          (!ct.trk || !ct.rpl) &amp;&amp; flags.unsnat_new ==
> 1</code> with
> > >> > > +          an action <code>ct_next(snat);</code>. There is extra
> match when
> > >> > > +          there is configured DGP
> > >> > > +          <code>outport == DGP &amp;&amp;
> is_chassis_resident(CHASSIS)</code>.
> > >> > > +        </li>
> > >> > > +
> > >> > >           <li>
> > >> > >             A priority-0 logical flow with match <code>1</code>
> has actions
> > >> > >           <code>next;</code>.
> > >> > > @@ -5332,6 +5382,15 @@ nd_ns {
> > >> > >           </p>
> > >> > >         </li>
> > >> > >
> > >> > > +      <li>
> > >> > > +        <p>
> > >> > > +          If the <code>optins:ct-commit-all</code> is set to
> <code>true</code>
> > >> > > +          the following flow is configured matching on <code>ip
> &amp;&amp;
> > >> > > +          ct.new &amp;&amp; flags.unsnat_new == 1</code> with
> > >> > > +          an action <code>ct_commit_to_zone(snat);</code>.
> > >> > > +        </p>
> > >> > > +      </li>
> > >> > > +
> > >> > >         <li>
> > >> > >           <p>
> > >> > >             A priority-0 logical flow with match <code>1</code>
> has actions
> > >> > > @@ -5412,6 +5471,16 @@ nd_ns {
> > >> > >           initiated from the internal or external network.
> > >> > >         </li>
> > >> > >
> > >> > > +      <li>
> > >> > > +        <p>
> > >> > > +          If the <code>optins:ct-commit-all</code> is set to
> <code>true</code>
> > >> > > +          the following flow is configured matching on <code>ip
> &amp;&amp;
> > >> > > +          ct.new &amp;&amp; flags.unsnat_new == 1 &amp;&amp;
> outport == DGP
> > >> > > +          &amp;&amp; is_chassis_resident(CHASSIS)</code> with
> > >> > > +          an action <code>ct_commit_to_zone(snat);</code>.
> > >> > > +        </p>
> > >> > > +      </li>
> > >> > > +
> > >> > >         <li>
> > >> > >           A priority-0 logical flow with match <code>1</code> has
> actions
> > >> > >           <code>next;</code>.
> > >> > > diff --git a/ovn-nb.xml b/ovn-nb.xml
> > >> > > index 07bd24fce..76be4d077 100644
> > >> > > --- a/ovn-nb.xml
> > >> > > +++ b/ovn-nb.xml
> > >> > > @@ -2955,6 +2955,14 @@ or
> > >> > >           option is not present the limit is not set and the zone
> limit is
> > >> > >           derived from OvS default datapath limit.
> > >> > >         </column>
> > >> > > +
> > >> > > +      <column name="options" key="ct-commit-all" type='{"type":
> "boolean"}'>
> > >> > > +          When enabled the LR will commit traffic in a zone that
> is stateful.
> > >> > > +          The traffic is not commited to both zones but it is
> selective based
> > >> > > +          whether there is stateful DNAT/SNAT or both. The
> commit all will
> > >> > > +          prevent issues with <code>ct.inv</code> packets as it
> will prevent
> > >> > > +          the commit of reply traffic, which could happen in
> some cases.
> > >> > > +      </column>
> > >> > >       </group>
> > >> > >
> > >> > >       <group title="Common Columns">
> > >> > > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> > >> > > index d486f042b..d0b841d61 100644
> > >> > > --- a/tests/ovn-northd.at
> > >> > > +++ b/tests/ovn-northd.at
> > >> > > @@ -14630,3 +14630,259 @@ check_row_count Port_Binding 1
> logical_port=tr-az4 type=patch
> > >> > >
> > >> > >   AT_CLEANUP
> > >> > >   ])
> > >> > > +
> > >> > > +OVN_FOR_EACH_NORTHD_NO_HV_PARALLELIZATION([
> > >> > > +AT_SETUP([ovn -- LR ct-commit-all])
> > >> > > +ovn_start
> > >> > > +
> > >> > > +check ovn-nbctl ls-add sw0
> > >> > > +check ovn-nbctl lr-add lr0
> > >> > > +check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01
> 10.0.0.1/24
> > >> > > +check ovn-nbctl lsp-add sw0 sw0-lr0
> > >> > > +check ovn-nbctl lsp-set-type sw0-lr0 router
> > >> > > +check ovn-nbctl lsp-set-addresses sw0-lr0 00:00:00:00:ff:01
> > >> > > +check ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
> > >> > > +check ovn-nbctl set logical_router lr0
> options:ct-commit-all="true"
> > >> > > +check ovn-nbctl --wait=sb sync
> > >> > > +
> > >> > > +check ovn-sbctl chassis-add gw1 geneve 127.0.0.1 \
> > >> > > +  -- set chassis gw1 other_config:ct-commit-to-zone="true" \
> > >> > > +  -- set chassis gw1 other_config:ct-next-zone="true"
> > >> > > +
> > >> > > +ovn-sbctl dump-flows lr0 > lr0flows
> > >> > > +AT_CAPTURE_FILE([lr0flows])
> > >> > > +
> > >> > > +AT_CHECK([grep "lr_in_post_unsnat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> > >> > > +  table=??(lr_in_post_unsnat  ), priority=0    , match=(1),
> action=(next;)
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([grep "lr_in_defrag" lr0flows | ovn_strip_lflows], [0],
> [dnl
> > >> > > +  table=??(lr_in_defrag       ), priority=0    , match=(1),
> action=(next;)
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([grep "lr_in_dnat" lr0flows | ovn_strip_lflows], [0],
> [dnl
> > >> > > +  table=??(lr_in_dnat         ), priority=0    , match=(1),
> action=(next;)
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([grep "lr_out_post_undnat" lr0flows |
> ovn_strip_lflows], [0], [dnl
> > >> > > +  table=??(lr_out_post_undnat ), priority=0    , match=(1),
> action=(next;)
> > >> > > +])
> > >> > > +
> > >> > > +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=120  , match=(nd_ns),
> action=(next;)
> > >> > > +])
> > >> > > +
> > >> > > +
> > >> > > +# Create a distributed gw port on lr0
> > >> > > +check ovn-nbctl ls-add public
> > >> > > +check ovn-nbctl lrp-add lr0 lr0-public 00:00:00:00:ff:02
> 172.168.0.10/24
> > >> > > +check ovn-nbctl lrp-set-gateway-chassis lr0-public gw1
> > >> > > +
> > >> > > +check ovn-nbctl lsp-add public public-lr0 \
> > >> > > +    -- set Logical_Switch_Port public-lr0 \
> > >> > > +        type=router options:router-port=lr0-public \
> > >> > > +    -- lsp-set-addresses public-lr0 router
> > >> > > +
> > >> > > +# Add SNAT
> > >> > > +check ovn-nbctl lr-nat-add lr0 snat 172.168.0.10 10.0.0.0/24
> > >> > > +
> > >> > > +ovn-sbctl dump-flows lr0 > lr0flows
> > >> > > +AT_CAPTURE_FILE([lr0flows])
> > >> > > +
> > >> > > +AT_CHECK([grep "lr_in_post_unsnat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> > >> > > +  table=??(lr_in_post_unsnat  ), priority=0    , match=(1),
> action=(next;)
> > >> > > +  table=??(lr_in_post_unsnat  ), priority=10   , match=(ip &&
> (!ct.trk || ct.new) && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(flags.unsnat_new = 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) && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), 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 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")),
> action=(ct_commit_to_zone(dnat);)
> > >> > > +])
> > >> > > +
> > >> > > +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.unsnat_new == 1 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), 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.unsnat_new == 1 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), 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(172.168.0.10);)
> > >> > > +])
> > >> > > +
> > >> > > +check ovn-nbctl lr-nat-del lr0
> > >> > > +
> > >> > > +# Add LB to lr0
> > >> > > +check ovn-nbctl lb-add lb0 172.168.0.100:8082 "10.0.0.50:82,
> 10.0.0.60:82"
> > >> > > +check ovn-nbctl lr-lb-add lr0 lb0
> > >> > > +check ovn-nbctl --wait=sb sync
> > >> > > +
> > >> > > +ovn-sbctl dump-flows lr0 > lr0flows
> > >> > > +AT_CAPTURE_FILE([lr0flows])
> > >> > > +
> > >> > > +AT_CHECK([grep "lr_in_post_unsnat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> > >> > > +  table=??(lr_in_post_unsnat  ), priority=0    , match=(1),
> action=(next;)
> > >> > > +  table=??(lr_in_post_unsnat  ), priority=10   , match=(ip &&
> (!ct.trk || ct.new) && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(flags.unsnat_new = 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) && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_next(dnat);)
> > >> > > +  table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 172.168.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 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")),
> action=(ct_commit_to_zone(dnat);)
> > >> > > +  table=??(lr_in_dnat         ), priority=120  , match=(ct.new
> && !ct.rel && ip4 && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8082 &&
> is_chassis_resident("cr-lr0-public")), action=(ct_lb_mark(backends=
> 10.0.0.50:82,10.0.0.60:82);)
> > >> > > +  table=??(lr_in_dnat         ), priority=50   , match=(ct.est
> && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted), action=(next;)
> > >> > > +  table=??(lr_in_dnat         ), priority=50   , match=(ct.rel
> && !ct.est && !ct.new && !ct.rpl), action=(ct_commit_nat;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.est
> && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.force_snat ==
> 1), action=(flags.force_snat_for_lb = 1; next;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.est
> && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.skip_snat ==
> 1), action=(flags.skip_snat_for_lb = 1; next;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.rel
> && !ct.est && !ct.new && !ct.rpl && ct_mark.force_snat == 1),
> action=(flags.force_snat_for_lb = 1; ct_commit_nat;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.rel
> && !ct.est && !ct.new && !ct.rpl && ct_mark.skip_snat == 1),
> action=(flags.skip_snat_for_lb = 1; ct_commit_nat;)
> > >> > > +])
> > >> > > +
> > >> > > +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.unsnat_new == 1 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), 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.unsnat_new == 1 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_commit_to_zone(snat);)
> > >> > > +  table=??(lr_out_snat        ), priority=120  , match=(nd_ns),
> action=(next;)
> > >> > > +])
> > >> > > +
> > >> > > +# Add SNAT again
> > >> > > +check ovn-nbctl lr-nat-add lr0 snat 172.168.0.10 10.0.0.0/24
> > >> > > +check ovn-nbctl --wait=sb sync
> > >> > > +
> > >> > > +ovn-sbctl dump-flows lr0 > lr0flows
> > >> > > +AT_CAPTURE_FILE([lr0flows])
> > >> > > +
> > >> > > +AT_CHECK([grep "lr_in_post_unsnat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> > >> > > +  table=??(lr_in_post_unsnat  ), priority=0    , match=(1),
> action=(next;)
> > >> > > +  table=??(lr_in_post_unsnat  ), priority=10   , match=(ip &&
> (!ct.trk || ct.new) && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(flags.unsnat_new = 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) && inport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), action=(ct_next(dnat);)
> > >> > > +  table=??(lr_in_defrag       ), priority=100  , match=(ip &&
> ip4.dst == 172.168.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 && inport == "lr0-public" && is_chassis_resident("cr-lr0-public")),
> action=(ct_commit_to_zone(dnat);)
> > >> > > +  table=??(lr_in_dnat         ), priority=120  , match=(ct.new
> && !ct.rel && ip4 && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8082 &&
> is_chassis_resident("cr-lr0-public")), action=(ct_lb_mark(backends=
> 10.0.0.50:82,10.0.0.60:82);)
> > >> > > +  table=??(lr_in_dnat         ), priority=50   , match=(ct.est
> && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted), action=(next;)
> > >> > > +  table=??(lr_in_dnat         ), priority=50   , match=(ct.rel
> && !ct.est && !ct.new && !ct.rpl), action=(ct_commit_nat;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.est
> && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.force_snat ==
> 1), action=(flags.force_snat_for_lb = 1; next;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.est
> && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.skip_snat ==
> 1), action=(flags.skip_snat_for_lb = 1; next;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.rel
> && !ct.est && !ct.new && !ct.rpl && ct_mark.force_snat == 1),
> action=(flags.force_snat_for_lb = 1; ct_commit_nat;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.rel
> && !ct.est && !ct.new && !ct.rpl && ct_mark.skip_snat == 1),
> action=(flags.skip_snat_for_lb = 1; ct_commit_nat;)
> > >> > > +])
> > >> > > +
> > >> > > +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.unsnat_new == 1 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), 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.unsnat_new == 1 && outport == "lr0-public" &&
> is_chassis_resident("cr-lr0-public")), 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(172.168.0.10);)
> > >> > > +])
> > >> > > +
> > >> > > +# Make the logical router as Gateway router
> > >> > > +check ovn-nbctl lrp-del-gateway-chassis lr0-public gw1
> > >> > > +check ovn-nbctl set logical_router lr0 options:chassis=gw1
> > >> > > +check ovn-nbctl --wait=sb sync
> > >> > > +
> > >> > > +ovn-sbctl dump-flows lr0 > lr0flows
> > >> > > +AT_CAPTURE_FILE([lr0flows])
> > >> > > +
> > >> > > +AT_CHECK([grep "lr_in_post_unsnat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> > >> > > +  table=??(lr_in_post_unsnat  ), priority=0    , match=(1),
> action=(next;)
> > >> > > +  table=??(lr_in_post_unsnat  ), priority=10   , match=(ip &&
> (!ct.trk || ct.new)), action=(flags.unsnat_new = 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.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 == 172.168.0.100 && tcp && tcp.dst == 8082),
> action=(ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82);)
> > >> > > +  table=??(lr_in_dnat         ), priority=50   , match=(ct.est
> && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted), action=(next;)
> > >> > > +  table=??(lr_in_dnat         ), priority=50   , match=(ct.rel
> && !ct.est && !ct.new && !ct.rpl), action=(ct_commit_nat;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.est
> && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.force_snat ==
> 1), action=(flags.force_snat_for_lb = 1; next;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.est
> && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.skip_snat ==
> 1), action=(flags.skip_snat_for_lb = 1; next;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.rel
> && !ct.est && !ct.new && !ct.rpl && ct_mark.force_snat == 1),
> action=(flags.force_snat_for_lb = 1; ct_commit_nat;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.rel
> && !ct.est && !ct.new && !ct.rpl && ct_mark.skip_snat == 1),
> action=(flags.skip_snat_for_lb = 1; ct_commit_nat;)
> > >> > > +])
> > >> > > +
> > >> > > +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.unsnat_new == 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.unsnat_new == 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);)
> > >> > > +])
> > >> > > +
> > >> > > +# Disable commit all for the router
> > >> > > +check ovn-nbctl remove logical_router lr0 options ct-commit-all
> > >> > > +check ovn-nbctl --wait=sb sync
> > >> > > +
> > >> > > +ovn-sbctl dump-flows lr0 > lr0flows
> > >> > > +AT_CAPTURE_FILE([lr0flows])
> > >> > > +
> > >> > > +AT_CHECK([grep "lr_in_post_unsnat" lr0flows | ovn_strip_lflows],
> [0], [dnl
> > >> > > +  table=??(lr_in_post_unsnat  ), priority=0    , match=(1),
> action=(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=100  , match=(ip &&
> ip4.dst == 172.168.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=120  , match=(ct.new
> && !ct.rel && ip4 && ip4.dst == 172.168.0.100 && tcp && tcp.dst == 8082),
> action=(ct_lb_mark(backends=10.0.0.50:82,10.0.0.60:82);)
> > >> > > +  table=??(lr_in_dnat         ), priority=50   , match=(ct.est
> && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted), action=(next;)
> > >> > > +  table=??(lr_in_dnat         ), priority=50   , match=(ct.rel
> && !ct.est && !ct.new && !ct.rpl), action=(ct_commit_nat;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.est
> && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.force_snat ==
> 1), action=(flags.force_snat_for_lb = 1; next;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.est
> && !ct.rel && !ct.new && !ct.rpl && ct_mark.natted && ct_mark.skip_snat ==
> 1), action=(flags.skip_snat_for_lb = 1; next;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.rel
> && !ct.est && !ct.new && !ct.rpl && ct_mark.force_snat == 1),
> action=(flags.force_snat_for_lb = 1; ct_commit_nat;)
> > >> > > +  table=??(lr_in_dnat         ), priority=70   , match=(ct.rel
> && !ct.est && !ct.new && !ct.rpl && ct_mark.skip_snat == 1),
> action=(flags.skip_snat_for_lb = 1; ct_commit_nat;)
> > >> > > +])
> > >> > > +
> > >> > > +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; )
> > >> > > +])
> > >> > > +
> > >> > > +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=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);)
> > >> > > +])
> > >> > > +
> > >> > > +AT_CLEANUP
> > >> > > +])
> > >> > > diff --git a/tests/system-ovn-kmod.at b/tests/system-ovn-kmod.at
> > >> > > index f7745b979..9ba631ecc 100644
> > >> > > --- a/tests/system-ovn-kmod.at
> > >> > > +++ b/tests/system-ovn-kmod.at
> > >> > > @@ -1172,3 +1172,559 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to
> query port patch-.*/d
> > >> > >   /connection dropped.*/d"])
> > >> > >   AT_CLEANUP
> > >> > >   ])
> > >> > > +
> > >> > > +OVN_FOR_EACH_NORTHD([
> > >> > > +AT_SETUP([LR DGP - ct-commit-all])
> > >> > > +AT_KEYWORDS([ovnnat])
> > >> > > +
> > >> > > +CHECK_CONNTRACK()
> > >> > > +CHECK_CONNTRACK_NAT()
> > >> > > +ovn_start
> > >> > > +OVS_TRAFFIC_VSWITCHD_START()
> > >> > > +ADD_BR([br-int])
> > >> > > +
> > >> > > +# Set external-ids in br-int needed for ovn-controller
> > >> > > +ovs-vsctl \
> > >> > > +        -- set Open_vSwitch . external-ids:system-id=hv1 \
> > >> > > +        -- set Open_vSwitch .
> external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
> > >> > > +        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve
> \
> > >> > > +        -- set Open_vSwitch .
> external-ids:ovn-encap-ip=169.0.0.1 \
> > >> > > +        -- set bridge br-int fail-mode=secure
> other-config:disable-in-band=true
> > >> > > +
> > >> > > +# Start ovn-controller
> > >> > > +start_daemon ovn-controller
> > >> > > +
> > >> > > +# Logical network:
> > >> > > +# One LR R1 with switches foo (192.168.1.0/24 fd11::/64), bar (
> 192.168.2.0/24 fd12::/64),
> > >> > > +# and alice (172.16.1.0/24 fd20::/64) connected to it.  The
> port between R1 and
> > >> > > +# alice is the router gateway port where the R1 NAT rules are
> applied.
> > >> > > +#
> > >> > > +#    foo -- R1 -- alice
> > >> > > +#           |
> > >> > > +#    bar ----
> > >> > > +
> > >> > > +check ovn-nbctl lr-add R1
> > >> > > +check ovn-nbctl set logical_router R1
> options:ct-commit-all="true"
> > >> > > +
> > >> > > +check ovn-nbctl ls-add foo
> > >> > > +check ovn-nbctl ls-add bar
> > >> > > +check ovn-nbctl ls-add alice
> > >> > > +
> > >> > > +check ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
> fd11::1/64
> > >> > > +check ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24
> fd12::1/64
> > >> > > +check ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24
> fd20::1/64 \
> > >> > > +    -- lrp-set-gateway-chassis alice hv1
> > >> > > +
> > >> > > +# Connect foo to R1
> > >> > > +check ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port
> rp-foo \
> > >> > > +    type=router options:router-port=foo \
> > >> > > +    -- lsp-set-addresses rp-foo router
> > >> > > +
> > >> > > +# Connect bar to R1
> > >> > > +check ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port
> rp-bar \
> > >> > > +    type=router options:router-port=bar \
> > >> > > +    -- lsp-set-addresses rp-bar router
> > >> > > +
> > >> > > +# Connect alice to R1
> > >> > > +check ovn-nbctl lsp-add alice rp-alice -- set
> Logical_Switch_Port rp-alice \
> > >> > > +    type=router options:router-port=alice \
> > >> > > +    -- lsp-set-addresses rp-alice router
> > >> > > +
> > >> > > +# Logical port 'foo1' in switch 'foo'.
> > >> > > +ADD_NAMESPACES(foo1)
> > >> > > +ADD_VETH(foo1, foo1, br-int, "fd11::2", "f0:00:00:01:02:03", \
> > >> > > +         "fd11::1", "nodad", "192.168.1.2/24", "192.168.1.1")
> > >> > > +check ovn-nbctl lsp-add foo foo1 \
> > >> > > +-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2 fd11::2"
> > >> > > +
> > >> > > +# Logical port 'foo2' in switch 'foo'.
> > >> > > +ADD_NAMESPACES(foo2)
> > >> > > +ADD_VETH(foo2, foo2, br-int, "fd11::3/64", "f0:00:00:01:02:06", \
> > >> > > +         "fd11::1", "nodad", "192.168.1.3/24", "192.168.1.1")
> > >> > > +check ovn-nbctl lsp-add foo foo2 \
> > >> > > +-- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3 fd11::3"
> > >> > > +
> > >> > > +# Logical port 'bar1' in switch 'bar'.
> > >> > > +ADD_NAMESPACES(bar1)
> > >> > > +ADD_VETH(bar1, bar1, br-int, "fd12::2/64", "f0:00:00:01:02:04", \
> > >> > > +         "fd12::1", "nodad", "192.168.2.2/24", "192.168.2.1")
> > >> > > +check ovn-nbctl lsp-add bar bar1 \
> > >> > > +-- lsp-set-addresses bar1 "f0:00:00:01:02:04 192.168.2.2 fd12::2"
> > >> > > +
> > >> > > +# Logical port 'alice1' in switch 'alice'.
> > >> > > +ADD_NAMESPACES(alice1)
> > >> > > +ADD_VETH(alice1, alice1, br-int, "fd20::2/64",
> "f0:00:00:01:02:05", \
> > >> > > +         "fd20::1", "nodad", "172.16.1.2/24", "172.16.1.1")
> > >> > > +check ovn-nbctl lsp-add alice alice1 \
> > >> > > +-- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2
> fd20::2"
> > >> > > +
> > >> > > +# Add DNAT and SNAT rules
> > >> > > +check ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3
> 192.168.1.2 foo1 00:00:02:02:03:04
> > >> > > +check ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4
> 192.168.2.2 bar1 00:00:02:02:03:05
> > >> > > +check ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::3 fd11::2 foo1
> 00:00:02:02:03:04
> > >> > > +check ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::4 fd12::2 bar1
> 00:00:02:02:03:05
> > >> > > +
> > >> > > +# Add a SNAT rule
> > >> > > +check ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.1.0/24
> > >> > > +check ovn-nbctl lr-nat-add R1 snat fd20::1 fd11::/64
> > >> > > +
> > >> > > +check ovn-nbctl --wait=hv sync
> > >> > > +OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep
> 'nat(src=172.16.1.1)'])
> > >> > > +OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep
> 'nat(src=fd20::1)'])
> > >> > > +
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 192.168.2.2 |
> FORMAT_PING], \
> > >> > > +[0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +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=172.16.1.2,dst=192.168.2.2,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 fd12::2 |
> FORMAT_PING], \
> > >> > > +[0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(fd12::2) | \
> > >> > > +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> > >> > >
> +icmpv6,orig=(src=fd20::2,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::2,id=<cleared>,type=129,code=0),zone=<cleared>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.2 |
> FORMAT_PING], \
> > >> > > +[0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(172.16.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=172.16.1.2,dst=172.16.1.4,id=<cleared>,type=0,code=0),zone=<cleared>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 fd20::2 |
> FORMAT_PING], \
> > >> > > +[0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(fd20::2) | \
> > >> > > +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> > >> > >
> +icmpv6,orig=(src=fd12::2,dst=fd20::2,id=<cleared>,type=128,code=0),reply=(src=fd20::2,dst=fd20::4,id=<cleared>,type=129,code=0),zone=<cleared>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +# East-West NAT: 'foo1' pings 'bar1' using 172.16.1.4.
> > >> > > +NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 |
> FORMAT_PING], \
> > >> > > +[0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +# Check conntrack entries.
> > >> > > +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>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 fd20::4 |
> FORMAT_PING], [0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +# Check conntrack entries.  First SNAT of 'foo1' address happens.
> > >> > > +# 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=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>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 |
> FORMAT_PING], [0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +# Check conntrack entries.
> > >> > > +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=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>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +# East-West NAT: 'foo2' pings 'bar1' using fd20::4.
> > >> > > +NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 fd20::4 |
> FORMAT_PING], [0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +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=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
> > >> > > +])
> > >> > > +
> > >> > > +OVS_APP_EXIT_AND_WAIT([ovn-controller])
> > >> > > +
> > >> > > +as ovn-sb
> > >> > > +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> > >> > > +
> > >> > > +as ovn-nb
> > >> > > +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> > >> > > +
> > >> > > +as northd
> > >> > > +OVS_APP_EXIT_AND_WAIT([ovn-northd])
> > >> > > +
> > >> > > +as
> > >> > > +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
> > >> > > +/connection dropped.*/d"])
> > >> > > +AT_CLEANUP
> > >> > > +])
> > >> > > +
> > >> > > +OVN_FOR_EACH_NORTHD([
> > >> > > +AT_SETUP([LR GW router - ct-commit-all])
> > >> > > +AT_KEYWORDS([ovnnat])
> > >> > > +
> > >> > > +CHECK_CONNTRACK()
> > >> > > +CHECK_CONNTRACK_NAT()
> > >> > > +ovn_start
> > >> > > +OVS_TRAFFIC_VSWITCHD_START()
> > >> > > +ADD_BR([br-int])
> > >> > > +
> > >> > > +# Set external-ids in br-int needed for ovn-controller
> > >> > > +ovs-vsctl \
> > >> > > +        -- set Open_vSwitch . external-ids:system-id=hv1 \
> > >> > > +        -- set Open_vSwitch .
> external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
> > >> > > +        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve
> \
> > >> > > +        -- set Open_vSwitch .
> external-ids:ovn-encap-ip=169.0.0.1 \
> > >> > > +        -- set bridge br-int fail-mode=secure
> other-config:disable-in-band=true
> > >> > > +
> > >> > > +# Start ovn-controller
> > >> > > +start_daemon ovn-controller
> > >> > > +
> > >> > > +# Logical network:
> > >> > > +# One LR R1 with switches foo (192.168.1.0/24 fd11::/64), bar (
> 192.168.2.0/24 fd12::/64),
> > >> > > +# and alice (172.16.1.0/24 fd20::/64) connected to it.  The
> port between R1 and
> > >> > > +# alice is the router gateway port where the R1 NAT rules are
> applied.
> > >> > > +#
> > >> > > +#    foo -- R1 -- alice
> > >> > > +#           |
> > >> > > +#    bar ----
> > >> > > +
> > >> > > +check ovn-nbctl lr-add R1
> > >> > > +check ovn-nbctl set logical_router R1
> options:ct-commit-all="true"
> > >> > > +
> > >> > > +check ovn-nbctl ls-add foo
> > >> > > +check ovn-nbctl ls-add bar
> > >> > > +check ovn-nbctl ls-add alice
> > >> > > +
> > >> > > +check ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
> fd11::1/64
> > >> > > +check ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24
> fd12::1/64
> > >> > > +check ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24
> fd20::1/64 \
> > >> > > +    -- set logical_router R1 options:chassis="hv1"
> > >> > > +
> > >> > > +# Connect foo to R1
> > >> > > +check ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port
> rp-foo \
> > >> > > +    type=router options:router-port=foo \
> > >> > > +    -- lsp-set-addresses rp-foo router
> > >> > > +
> > >> > > +# Connect bar to R1
> > >> > > +check ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port
> rp-bar \
> > >> > > +    type=router options:router-port=bar \
> > >> > > +    -- lsp-set-addresses rp-bar router
> > >> > > +
> > >> > > +# Connect alice to R1
> > >> > > +check ovn-nbctl lsp-add alice rp-alice -- set
> Logical_Switch_Port rp-alice \
> > >> > > +    type=router options:router-port=alice \
> > >> > > +    -- lsp-set-addresses rp-alice router
> > >> > > +
> > >> > > +# Logical port 'foo1' in switch 'foo'.
> > >> > > +ADD_NAMESPACES(foo1)
> > >> > > +ADD_VETH(foo1, foo1, br-int, "fd11::2", "f0:00:00:01:02:03", \
> > >> > > +         "fd11::1", "nodad", "192.168.1.2/24", "192.168.1.1")
> > >> > > +check ovn-nbctl lsp-add foo foo1 \
> > >> > > +-- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2 fd11::2"
> > >> > > +
> > >> > > +# Logical port 'foo2' in switch 'foo'.
> > >> > > +ADD_NAMESPACES(foo2)
> > >> > > +ADD_VETH(foo2, foo2, br-int, "fd11::3/64", "f0:00:00:01:02:06", \
> > >> > > +         "fd11::1", "nodad", "192.168.1.3/24", "192.168.1.1")
> > >> > > +check ovn-nbctl lsp-add foo foo2 \
> > >> > > +-- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3 fd11::3"
> > >> > > +
> > >> > > +# Logical port 'bar1' in switch 'bar'.
> > >> > > +ADD_NAMESPACES(bar1)
> > >> > > +ADD_VETH(bar1, bar1, br-int, "fd12::2/64", "f0:00:00:01:02:04", \
> > >> > > +         "fd12::1", "nodad", "192.168.2.2/24", "192.168.2.1")
> > >> > > +check ovn-nbctl lsp-add bar bar1 \
> > >> > > +-- lsp-set-addresses bar1 "f0:00:00:01:02:04 192.168.2.2 fd12::2"
> > >> > > +
> > >> > > +# Logical port 'alice1' in switch 'alice'.
> > >> > > +ADD_NAMESPACES(alice1)
> > >> > > +ADD_VETH(alice1, alice1, br-int, "fd20::2/64",
> "f0:00:00:01:02:05", \
> > >> > > +         "fd20::1", "nodad", "172.16.1.2/24", "172.16.1.1")
> > >> > > +check ovn-nbctl lsp-add alice alice1 \
> > >> > > +-- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2
> fd20::2"
> > >> > > +
> > >> > > +# Add DNAT and SNAT rules
> > >> > > +check ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3
> 192.168.1.2 foo1 00:00:02:02:03:04
> > >> > > +check ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4
> 192.168.2.2 bar1 00:00:02:02:03:05
> > >> > > +check ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::3 fd11::2 foo1
> 00:00:02:02:03:04
> > >> > > +check ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::4 fd12::2 bar1
> 00:00:02:02:03:05
> > >> > > +
> > >> > > +# Add a SNAT rule
> > >> > > +check ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.1.0/24
> > >> > > +check ovn-nbctl lr-nat-add R1 snat fd20::1 fd11::/64
> > >> > > +
> > >> > > +check ovn-nbctl --wait=hv sync
> > >> > > +OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep
> 'nat(src=172.16.1.1)'])
> > >> > > +OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep
> 'nat(src=fd20::1)'])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 192.168.2.2 |
> FORMAT_PING], \
> > >> > > +[0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +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=172.16.1.3,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])
> > >> > > +NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 fd12::2 |
> FORMAT_PING], \
> > >> > > +[0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(fd12::2) | \
> > >> > > +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> > >> > >
> +icmpv6,orig=(src=fd11::2,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd11::2,id=<cleared>,type=129,code=0),zone=<cleared>
> > >> > >
> +icmpv6,orig=(src=fd11::2,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 192.168.2.2 |
> FORMAT_PING], \
> > >> > > +[0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +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=172.16.1.1,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])
> > >> > > +NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 fd12::2 |
> FORMAT_PING], \
> > >> > > +[0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(fd12::2) | \
> > >> > > +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> > >> > >
> +icmpv6,orig=(src=fd11::3,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd11::3,id=<cleared>,type=129,code=0),zone=<cleared>
> > >> > >
> +icmpv6,orig=(src=fd11::3,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 192.168.1.3 |
> FORMAT_PING], \
> > >> > > +[0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(192.168.1.3) | \
> > >> > > +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=172.16.1.4,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])
> > >> > > +NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 fd11::3 |
> FORMAT_PING], \
> > >> > > +[0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/dump-conntrack | grep icmp |
> FORMAT_CT(fd11::3) | \
> > >> > > +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> > >> > >
> +icmpv6,orig=(src=fd12::2,dst=fd11::3,id=<cleared>,type=128,code=0),reply=(src=fd11::3,dst=fd12::2,id=<cleared>,type=129,code=0),zone=<cleared>
> > >> > >
> +icmpv6,orig=(src=fd12::2,dst=fd11::3,id=<cleared>,type=128,code=0),reply=(src=fd11::3,dst=fd20::4,id=<cleared>,type=129,code=0),zone=<cleared>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 |
> FORMAT_PING], \
> > >> > > +[0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +# Check conntrack entries.
> > >> > > +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=192.168.1.2,dst=172.16.1.4,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/dump-conntrack | grep icmp |
> FORMAT_CT(172.16.1.3) | \
> > >> > > +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=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 fd20::4 |
> FORMAT_PING], [0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +# Check conntrack entries.
> > >> > > +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=fd12::2,dst=fd11::2,id=<cleared>,type=129,code=0),zone=<cleared>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::3) |
> \
> > >> > > +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> > >> > >
> +icmpv6,orig=(src=fd11::2,dst=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +# East-West NAT: 'foo2' pings 'bar1' using 172.16.1.4.
> > >> > > +NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 |
> FORMAT_PING], [0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +# Check conntrack entries.
> > >> > > +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=192.168.1.3,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>
> > >> > > +])
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/dump-conntrack |
> FORMAT_CT(172.16.1.4) | \
> > >> > > +sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
> > >> > >
> +icmp,orig=(src=192.168.1.3,dst=172.16.1.4,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])
> > >> > > +# East-West NAT: 'foo2' pings 'bar1' using fd20::4.
> > >> > > +NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 fd20::4 |
> FORMAT_PING], [0], [dnl
> > >> > > +3 packets transmitted, 3 received, 0% packet loss, time 0ms
> > >> > > +])
> > >> > > +
> > >> > > +# Check conntrack entries.
> > >> > > +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=fd12::2,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
> > >> > > +])
> > >> > > +
> > >> > > +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::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd11::3,id=<cleared>,type=129,code=0),zone=<cleared>
> > >> > > +])
> > >> > > +
> > >> > > +OVS_APP_EXIT_AND_WAIT([ovn-controller])
> > >> > > +
> > >> > > +as ovn-sb
> > >> > > +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> > >> > > +
> > >> > > +as ovn-nb
> > >> > > +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> > >> > > +
> > >> > > +as northd
> > >> > > +OVS_APP_EXIT_AND_WAIT([ovn-northd])
> > >> > > +
> > >> > > +as
> > >> > > +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
> > >> > > +/connection dropped.*/d"])
> > >> > > +AT_CLEANUP
> > >> > > +])
> > >> > > +
> > >> > > +OVN_FOR_EACH_NORTHD([
> > >> > > +AT_SETUP([Commit all - UDN])
> > >> > > +AT_KEYWORDS([ovnnat])
> > >> > > +
> > >> > > +CHECK_CONNTRACK()
> > >> > > +CHECK_CONNTRACK_NAT()
> > >> > > +ovn_start
> > >> > > +OVS_TRAFFIC_VSWITCHD_START()
> > >> > > +ADD_BR([br-int])
> > >> > > +
> > >> > > +# Set external-ids in br-int needed for ovn-controller
> > >> > > +check ovs-vsctl \
> > >> > > +        -- set Open_vSwitch . external-ids:system-id=hv1 \
> > >> > > +        -- set Open_vSwitch .
> external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
> > >> > > +        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve
> \
> > >> > > +        -- set Open_vSwitch .
> external-ids:ovn-encap-ip=169.0.0.1 \
> > >> > > +        -- set bridge br-int fail-mode=secure
> other-config:disable-in-band=true
> > >> > > +
> > >> > > +# Start ovn-controller
> > >> > > +start_daemon ovn-controller
> > >> > > +
> > >> > > +check ovn-nbctl lr-add lr
> > >> > > +check ovn-nbctl ls-add ls
> > >> > > +
> > >> > > +check ovn-nbctl lrp-add lr lr-ls 00:00:00:00:00:01
> 192.168.100.1/24 \
> > >> > > +    -- lrp-set-gateway-chassis lr-ls hv1
> > >> > > +check ovn-nbctl lsp-add ls ls-lr -- set Logical_Switch_Port
> ls-lr \
> > >> > > +    type=router options:router-port=lr-ls \
> > >> > > +    -- lsp-set-addresses ls-lr router
> > >> > > +
> > >> > > +ADD_NAMESPACES(pod)
> > >> > > +ADD_VETH(pod, pod, br-int, "192.168.100.5/24",
> "00:00:00:00:00:05", "192.168.100.1")
> > >> > > +check ovn-nbctl lsp-add ls pod \
> > >> > > +    -- lsp-set-addresses pod "00:00:00:00:00:05 192.168.100.5"
> > >> > > +
> > >> > > +ADD_NAMESPACES(mgmt)
> > >> > > +ADD_VETH(mgmt, mgmt, br-int, "192.168.100.2/24",
> "00:00:00:00:00:02", "192.168.100.1")
> > >> > > +NS_EXEC([mgmt], [ip addr add 172.16.100.2/24 dev mgmt])
> > >> > > +check ovn-nbctl lsp-add ls mgmt \
> > >> > > +    -- lsp-set-addresses mgmt "00:00:00:00:00:02 192.168.100.2"
> > >> > > +
> > >> > > +check check ovn-nbctl --policy="src-ip" lr-route-add lr
> 192.168.100.0/24 192.168.100.2
> > >> > > +check ovn-nbctl lb-add lb0 172.16.0.5:5656 192.168.100.5:2323
> udp
> > >> > > +check ovn-nbctl lb-add lb1 172.16.0.5:5657 192.168.100.5:2324
> tcp
> > >> > > +check ovn-nbctl ls-lb-add ls lb0
> > >> > > +check ovn-nbctl ls-lb-add ls lb1
> > >> > > +
> > >> > > +check ovn-nbctl --match="eth.dst == 00:00:00:00:00:02"
> lr-nat-add lr snat 172.16.0.2 192.168.100.0/24
> > >> > > +check ovn-nbctl set logical_router lr
> options:ct-commit-all="true"
> > >> > > +
> > >> > > +wait_for_ports_up
> > >> > > +check ovn-nbctl --wait=hv sync
> > >> > > +
> > >> > > +AT_CHECK([ovs-appctl dpctl/flush-conntrack])
> > >> > > +
> > >> > > +NETNS_START_TCPDUMP([mgmt], [-vnne "ip or icmp"], [mgmt])
> > >> > > +NETNS_START_TCPDUMP([pod], [-vnne "ip or icmp"], [pod])
> > >> > > +
> > >> > > +echo -e "Hello UDP\nHello UDP" > udp.expected
> > >> > > +echo -e "Hello TCP\nHello TCP" > tcp.expected
> > >> > > +
> > >> > > +NETNS_DAEMONIZE([pod], [nc -e /bin/cat -v -l -u -o
> server_udp.log 192.168.100.5 2323], [nc1.pid])
> > >> > > +NETNS_DAEMONIZE([pod], [nc -e /bin/cat -v -l -o server_tcp.log
> 192.168.100.5 2324], [nc2.pid])
> > >> > > +
> > >> > > +NS_CHECK_EXEC([mgmt], [(echo "Hello UDP"; sleep 3) | nc -u -s
> 172.16.100.2 -o client_udp.log 172.16.0.5 5656], [0], [ignore], [ignore])
> > >> > > +check cmp server_udp.log udp.expected
> > >> > > +check cmp client_udp.log udp.expected
> > >> > > +
> > >> > > +NS_CHECK_EXEC([mgmt], [(echo "Hello TCP"; sleep 3) | nc -s
> 172.16.100.2 -o client_tcp.log 172.16.0.5 5657], [0], [ignore], [ignore])
> > >> > > +check cmp server_tcp.log tcp.expected
> > >> > > +check cmp client_tcp.log tcp.expected
> > >> > > +
> > >> > > +OVS_APP_EXIT_AND_WAIT([ovn-controller])
> > >> > > +
> > >> > > +as ovn-sb
> > >> > > +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> > >> > > +
> > >> > > +as ovn-nb
> > >> > > +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> > >> > > +
> > >> > > +as northd
> > >> > > +OVS_APP_EXIT_AND_WAIT([ovn-northd])
> > >> > > +
> > >> > > +as
> > >> > > +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
> > >> > > +/connection dropped.*/d"])
> > >> > > +AT_CLEANUP
> > >> > > +])
> > >> >
> > >> > _______________________________________________
> > >> > dev mailing list
> > >> > [email protected]
> > >> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to