Hi all,

I tested with the following two lines before AT_CLEANUP, and the leaks were
detected.

as northd
OVS_APP_EXIT_AND_WAIT([ovn-northd])


Regards,
Lucas

Em seg., 10 de nov. de 2025 às 10:22, Lucas Vargas Dias <
[email protected]> escreveu:

> Hi Dumitru
>
> Em seg., 10 de nov. de 2025 às 10:18, Dumitru Ceara <[email protected]>
> escreveu:
>
>> On 11/10/25 2:11 PM, Lucas Vargas Dias wrote:
>> > Hi,
>>
>> Hi Lucas,
>>
>> > Thanks for review
>> >
>> > Em seg., 10 de nov. de 2025 às 09:50, Dumitru Ceara <[email protected]>
>> > escreveu:
>> >
>> >> On 10/22/25 4:05 PM, Lucas Vargas Dias via dev wrote:
>> >>> FIN or FIN-ACK packets from client was dropping because the client
>> >>> side was using conntrack. Connection is in SYN_SENT state because the
>> >>> response from backends bypass the conntrack, when client sends a FIN
>> >>> or FIN-ACK, the conntrack is invalid and packet is dropped.
>> >>> To fix it, remove the client side from conntrack, calculating the hash
>> >>> from packet to choice the backend.
>> >>> REG_IDX_LB_STATELESS is used to store the index from backend.
>> >>>
>> >>> Signed-off-by: Lucas Vargas Dias <[email protected]>
>> >>> ---
>> >>
>> >> Hi Lucas,
>> >>
>> >> Thanks for the patch!
>> >>
>> >>>  northd/lb.c         |   3 +
>> >>>  northd/lb.h         |   1 +
>> >>>  northd/northd.c     | 278
>> +++++++++++++++++++++++++++++++-------------
>> >>>  tests/multinode.at  |  99 ++++++++++------
>> >>>  tests/ovn-northd.at | 218 ++++++++++++++++++++++++++++++++--
>> >>>  5 files changed, 467 insertions(+), 132 deletions(-)
>> >>>
>> >>> diff --git a/northd/lb.c b/northd/lb.c
>> >>> index 919557ec4..2266d76e8 100644
>> >>> --- a/northd/lb.c
>> >>> +++ b/northd/lb.c
>> >>> @@ -358,6 +358,9 @@ ovn_northd_lb_init(struct ovn_northd_lb *lb,
>> >>>      }
>> >>>      lb->affinity_timeout = affinity_timeout;
>> >>>
>> >>> +    lb->use_stateless_nat = smap_get_bool(&nbrec_lb->options,
>> >>> +                                          "use_stateless_nat",
>> false);
>> >>> +
>> >>>      const char *snat_ip = smap_get(&nbrec_lb->options,
>> >> "hairpin_snat_ip");
>> >>>
>> >>>      if (snat_ip && validate_snap_ip_address(snat_ip)) {
>> >>> diff --git a/northd/lb.h b/northd/lb.h
>> >>> index 43a8a1850..53dc4abf0 100644
>> >>> --- a/northd/lb.h
>> >>> +++ b/northd/lb.h
>> >>> @@ -75,6 +75,7 @@ struct ovn_northd_lb {
>> >>>      bool health_checks;
>> >>>
>> >>>      char *hairpin_snat_ip;
>> >>> +    bool use_stateless_nat;
>> >>>  };
>> >>>
>> >>>  /* ovn-northd specific backend information. */
>> >>> diff --git a/northd/northd.c b/northd/northd.c
>> >>> index 55e31659f..9da995cea 100644
>> >>> --- a/northd/northd.c
>> >>> +++ b/northd/northd.c
>> >>> @@ -157,6 +157,7 @@ static bool vxlan_mode;
>> >>>  #define REG_LB_PORT "reg2[0..15]"
>> >>>  #define REG_CT_TP_DST "reg1[0..15]"
>> >>>  #define REG_CT_PROTO "reg1[16..23]"
>> >>> +#define REG_IDX_LB_STATELESS "reg1[0..15]"
>> >>>
>> >>>  /* Registers for ACL evaluation */
>> >>>  #define REGBIT_ACL_VERDICT_ALLOW "reg8[16]"
>> >>> @@ -12416,6 +12417,159 @@ lrouter_use_common_zone(const struct
>> >> ovn_datapath *od)
>> >>>      return !od->is_gw_router && use_common_zone;
>> >>>  }
>> >>>
>> >>> +static void
>> >>> +build_lrouter_flows_for_lb_stateless(struct lrouter_nat_lb_flows_ctx
>> >> *ctx,
>> >>> +                                     struct ovn_datapath *od,
>> >>> +                                     struct lflow_ref *lflow_ref,
>> >>> +                                     struct ovn_port *dgp,
>> >>> +                                     const char *meter)
>> >>> +{
>> >>> +    /* (NOTE) dnat_action: Add a new rule lr_in_dnat with backend IP
>> >>> +     * and port action to skip conntrack completely. It is based on
>> >>> +     * REG_IDX_LB_STATELESS which was calculated in lr_in_ct_extract.
>> >>> +     * So, if the packet has VIP IP destination and port
>> >>> +     * (if port was configured), it selecs a backend based on
>> >>> +     * REG_IDX_LB_STATELESS. It works to multi-chassis and avoid to
>> >>> +     * sync conntrack betweem them.
>> >>> +     */
>> >>> +    struct ds new_action_stateless_nat = DS_EMPTY_INITIALIZER;
>> >>> +    struct ds new_match_stateless_nat = DS_EMPTY_INITIALIZER;
>> >>> +    if (!vector_is_empty(&ctx->lb_vip->backends) ||
>> >>> +        !ctx->lb_vip->empty_backend_rej) {
>> >>> +        ds_put_format(&new_match_stateless_nat,
>> >> "is_chassis_resident(%s)",
>> >>> +                      dgp->cr_port->json_key);
>> >>> +    }
>> >>> +
>> >>> +    bool ipv4 = ctx->lb_vip->address_family == AF_INET;
>> >>> +    const char *ip_match = ipv4 ? "ip4" : "ip6";
>> >>> +    ds_put_format(&new_match_stateless_nat, " && %s && %s.dst == %s",
>> >>> +                   ip_match, ip_match, ctx->lb_vip->vip_str);
>> >>
>> >> Nit: should be aligned one space to the left, under '&'.
>> >>
>> >
>> > I agree.
>> >
>> >
>> >>
>> >>> +    if (ctx->lb_vip->port_str) {
>> >>> +        ds_put_format(&new_match_stateless_nat,
>> >>> +                      " && %s && %s.dst == %s",
>> >>> +                      ctx->lb->proto, ctx->lb->proto,
>> >>> +                      ctx->lb_vip->port_str);
>> >>> +    }
>> >>> +
>> >>> +    const struct ovn_lb_backend *backend;
>> >>> +    if (vector_len(&ctx->lb_vip->backends) == 1) {
>> >>> +        backend = vector_get_ptr(&ctx->lb_vip->backends, 0);
>> >>> +        ds_put_format(&new_action_stateless_nat, "%s.dst = %s; ",
>> >>> +                      ip_match, backend->ip_str);
>> >>> +        if (ctx->lb_vip->port_str) {
>> >>> +            ds_put_format(&new_action_stateless_nat, "%s.dst = %s; ",
>> >>> +                          ctx->lb->proto, backend->port_str);
>> >>> +        }
>> >>> +        ds_put_format(&new_action_stateless_nat, "next;");
>> >>> +        ovn_lflow_add_with_hint__(ctx->lflows, od, S_ROUTER_IN_DNAT,
>> >>> +                                  ctx->prio,
>> >>> +                                  ds_cstr(&new_match_stateless_nat),
>> >>> +                                  ds_cstr(&new_action_stateless_nat),
>> >>> +                                  NULL, meter,
>> &ctx->lb->nlb->header_,
>> >>> +                                  lflow_ref);
>> >>> +    }
>> >>> +    size_t match_len = new_match_stateless_nat.length;
>> >>> +    size_t i = 0;
>> >>> +    VECTOR_FOR_EACH_PTR (&ctx->lb_vip->backends, backend) {
>> >>> +        if (vector_len(&ctx->lb_vip->backends) <= 1) {
>> >>> +            break;
>> >>> +        }
>> >>> +        ds_put_format(&new_match_stateless_nat, " && "
>> >>> +            REG_IDX_LB_STATELESS" == ""%"PRIuSIZE, i++);
>> >>> +        ds_put_format(&new_action_stateless_nat, "%s.dst = %s; ",
>> >>> +                      ip_match, backend->ip_str);
>> >>> +        if (ctx->lb_vip->port_str) {
>> >>> +            ds_put_format(&new_action_stateless_nat, "%s.dst = %s; ",
>> >>> +                          ctx->lb->proto, backend->port_str);
>> >>> +        }
>> >>> +        ds_put_format(&new_action_stateless_nat, "next;");
>> >>> +        ovn_lflow_add_with_hint__(ctx->lflows, od, S_ROUTER_IN_DNAT,
>> >>> +                                  ctx->prio,
>> >>> +                                  ds_cstr(&new_match_stateless_nat),
>> >>> +                                  ds_cstr(&new_action_stateless_nat),
>> >>> +                                  NULL, meter,
>> &ctx->lb->nlb->header_,
>> >>> +                                  lflow_ref);
>> >>> +        ds_clear(&new_action_stateless_nat);
>> >>> +        ds_truncate(&new_match_stateless_nat, match_len);
>> >>> +    }
>> >>> +
>> >>> +
>> >>
>> >> We leak 'new_match_stateless_nat' and 'new_action_stateless_nat' here.
>> >> As the system and unit tests were green it means we're not testing this
>> >> in non-multinode tests.  Is that right?  Can we add a small
>> >> ovn-northd.at test for this scenario too?
>> >>
>> >
>> > Actually, we are testing in "Load balancer with Distributed Gateway
>> Ports
>> > (LB + DGP + NAT Stateless".
>> >
>>
>> Hmm, that's weird, we run tests with address sanitizer enabled and that
>> should detect the memory leak.  And unless I completely misread the code
>> there is a leak here in all cases.
>>
>
> You're right, there's a leak. I didn't understand why it didn't find the
> leak.
>
>
>> >>
>> >>> +    if (vector_is_empty(&ctx->lb_vip->backends)) {
>> >>> +        return;
>> >>> +    }
>> >>> +
>> >>> +    size_t undnat_match_len = ctx->undnat_match->length;
>> >>> +    struct ds undnat_action = DS_EMPTY_INITIALIZER;
>> >>> +    struct ds snat_action = DS_EMPTY_INITIALIZER;
>> >>> +
>> >>> +
>> >>> +    /* undnat_action: Just follows the pipeline in the lr_out_undenat
>> >> NAT rule.
>> >>> +     */
>> >>> +    ds_put_format(&undnat_action, "next;");
>> >>> +
>> >>> +    /* We need to centralize the LB traffic to properly perform
>> >>> +     * the undnat stage.
>> >>> +     */
>> >>> +    ds_put_format(ctx->undnat_match, ") && outport == %s",
>> >> dgp->json_key);
>> >>> +    ds_clear(ctx->gw_redir_action);
>> >>> +    ds_put_format(ctx->gw_redir_action, "outport = %s; next;",
>> >>> +                  dgp->cr_port->json_key);
>> >>> +
>> >>> +    ovn_lflow_add_with_hint(ctx->lflows, od, S_ROUTER_IN_GW_REDIRECT,
>> >>> +                            200, ds_cstr(ctx->undnat_match),
>> >>> +                            ds_cstr(ctx->gw_redir_action),
>> >>> +                            &ctx->lb->nlb->header_,
>> >>> +                            lflow_ref);
>> >>> +    ds_truncate(ctx->undnat_match, undnat_match_len);
>> >>> +
>> >>> +    ds_put_format(ctx->undnat_match, ") && (inport == %s || outport
>> ==
>> >> %s)"
>> >>> +                  " && is_chassis_resident(%s)", dgp->json_key,
>> >> dgp->json_key,
>> >>> +                  dgp->cr_port->json_key);
>> >>
>> >> Nit: this reads a bit weird to me, I'd write it as:
>> >>
>> >>     ds_put_format(ctx->undnat_match, ") && (inport == %s || outport ==
>> %s)"
>> >>                                      " && is_chassis_resident(%s)",
>> >>                   dgp->json_key, dgp->json_key,
>> >>                   dgp->cr_port->json_key);
>> >>
>> >>
>> > I agree.
>> >
>> >
>> >>> +
>> >>> +    /* Use the LB protocol as matching criteria for out undnat and
>> snat
>> >> when
>> >>> +     * creating LBs with stateless NAT. */
>> >>> +    ds_put_format(ctx->undnat_match, " && %s", ctx->lb->proto);
>> >>> +
>> >>> +    ovn_lflow_add_with_hint(ctx->lflows, od, S_ROUTER_OUT_UNDNAT,
>> 120,
>> >>> +                            ds_cstr(ctx->undnat_match),
>> >>> +                            ds_cstr(&undnat_action),
>> >> &ctx->lb->nlb->header_,
>> >>> +                            lflow_ref);
>> >>> +
>> >>> +    /* (NOTE) snat_action: Add a new rule lr_out_snat with LB VIP as
>> >> source
>> >>> +     * IP action to perform stateless NAT pipeline completely when
>> the
>> >>> +     * outgoing packet is redirected to a chassis that does not have
>> an
>> >>> +     * active conntrack entry. Otherwise, it will not be SNATed by
>> the
>> >>> +     * ct_lb action because it does not refer to a valid created
>> flow.
>> >> The
>> >>> +     * use case for responding to a packet in different chassis is
>> >> multipath
>> >>> +     * via ECMP. So, the LB lr_out_snat is created with a lower
>> >> priority than
>> >>> +     * the other router pipeline entries, in this case, if the packet
>> >> is not
>> >>> +     * SNATed by ct_lb (conntrack lost), it will be SNATed by the LB
>> >>> +     * stateless NAT rule. Also, SNAT is performed only when the
>> packet
>> >>> +     * matches the configured LB backend IPs, ports and protocols.
>> >> Otherwise,
>> >>> +     * the packet will be forwarded without SNAted interference.
>> >>> +     */
>> >>> +    if (ctx->lb_vip->port_str) {
>> >>> +        ds_put_format(&snat_action, "%s.src = %s; %s.src = %s;
>> next;",
>> >>> +                      ctx->lb_vip->address_family == AF_INET6 ?
>> >>> +                      "ip6" : "ip4",
>> >>
>> >> We have the 'bool ipv4' variable, set earlier in the function.  This
>> could
>> >> be:
>> >>
>> >> ipv4 ? "ip4" : "ip6",
>> >>
>> >> I agree.
>> >
>> >
>> >>> +                      ctx->lb_vip->vip_str, ctx->lb->proto,
>> >>> +                      ctx->lb_vip->port_str);
>> >>> +    } else {
>> >>> +        ds_put_format(&snat_action, "%s.src = %s; next;",
>> >>> +                      ctx->lb_vip->address_family == AF_INET6 ?
>> >>> +                      "ip6" : "ip4",
>> >>
>> >> Same here.
>> >>
>> >> I agree.
>> >
>> >
>> >>> +                      ctx->lb_vip->vip_str);
>> >>> +    }
>> >>> +    ovn_lflow_add_with_hint(ctx->lflows, od, S_ROUTER_OUT_SNAT, 160,
>> >>> +                            ds_cstr(ctx->undnat_match),
>> >>> +                            ds_cstr(&snat_action),
>> >> &ctx->lb->nlb->header_,
>> >>> +                            lflow_ref);
>> >>> +
>> >>> +    ds_truncate(ctx->undnat_match, undnat_match_len);
>> >>> +    ds_destroy(&undnat_action);
>> >>> +    ds_destroy(&snat_action);
>> >>> +}
>> >>> +
>> >>>  static void
>> >>>  build_distr_lrouter_nat_flows_for_lb(struct lrouter_nat_lb_flows_ctx
>> >> *ctx,
>> >>>                                       enum lrouter_nat_lb_flow_type
>> type,
>> >>> @@ -12424,60 +12578,34 @@ build_distr_lrouter_nat_flows_for_lb(struct
>> >> lrouter_nat_lb_flows_ctx *ctx,
>> >>>                                       struct ovn_port *dgp,
>> >>>                                       bool stateless_nat)
>> >>>  {
>> >>> -    struct ds dnat_action = DS_EMPTY_INITIALIZER;
>> >>> -
>> >>>      /* Store the match lengths, so we can reuse the ds buffer. */
>> >>>      size_t new_match_len = ctx->new_match->length;
>> >>>      size_t undnat_match_len = ctx->undnat_match->length;
>> >>>
>> >>> -    /* (NOTE) dnat_action: Add the first LB backend IP as a
>> destination
>> >>> -     * action of the lr_in_dnat NAT rule. Including the backend IP is
>> >> useful
>> >>> -     * for accepting packets coming from a chassis that does not have
>> >>> -     * previously established conntrack entries. This means that the
>> >> actions
>> >>> -     * (ip4.dst + ct_lb_mark) are executed in addition and ip4.dst is
>> >> not
>> >>> -     * useful when traffic passes through the same chassis for
>> >> ingress/egress
>> >>> -     * packets. However, the actions are complementary in cases where
>> >> traffic
>> >>> -     * enters from one chassis, the ack response comes from another
>> >> chassis,
>> >>> -     * and the final ack step of the TCP handshake comes from the
>> first
>> >>> -     * chassis used. Without using stateless NAT, the connection will
>> >> not be
>> >>> -     * established because the return packet followed a path through
>> >> another
>> >>> -     * chassis and only ct_lb_mark will not be able to receive the
>> ack
>> >> and
>> >>> -     * forward it to the right backend. With using stateless NAT, the
>> >> packet
>> >>> -     * will be accepted and forwarded to the same backend that
>> >> corresponds to
>> >>> -     * the previous conntrack entry that is in the SYN_SENT state
>> >>> -     * (created by ct_lb_mark for the first rcv packet in this flow).
>> >>> -     */
>> >>> -    if (stateless_nat) {
>> >>> -        if (!vector_is_empty(&ctx->lb_vip->backends)) {
>> >>> -            const struct ovn_lb_backend *backend =
>> >>> -                vector_get_ptr(&ctx->lb_vip->backends, 0);
>> >>> -            bool ipv6 = !IN6_IS_ADDR_V4MAPPED(&backend->ip);
>> >>> -            ds_put_format(&dnat_action, "%s.dst = %s; ", ipv6 ?
>> "ip6" :
>> >> "ip4",
>> >>> -                          backend->ip_str);
>> >>> -        }
>> >>> -    }
>> >>> -    ds_put_format(&dnat_action, "%s", ctx->new_action[type]);
>> >>> -
>> >>>      const char *meter = NULL;
>> >>>
>> >>>      if (ctx->reject) {
>> >>>          meter = copp_meter_get(COPP_REJECT, od->nbr->copp,
>> >> ctx->meter_groups);
>> >>>      }
>> >>>
>> >>> +    if (stateless_nat) {
>> >>> +        return build_lrouter_flows_for_lb_stateless(ctx, od,
>> lflow_ref,
>> >>> +            dgp, meter);
>> >>> +    }
>> >>> +
>> >>>      if (!vector_is_empty(&ctx->lb_vip->backends) ||
>> >>>          !ctx->lb_vip->empty_backend_rej) {
>> >>>          ds_put_format(ctx->new_match, " && is_chassis_resident(%s)",
>> >>>                        dgp->cr_port->json_key);
>> >>>      }
>> >>>
>> >>> -    ovn_lflow_add_with_hint__(ctx->lflows, od, S_ROUTER_IN_DNAT,
>> >> ctx->prio,
>> >>> -                              ds_cstr(ctx->new_match),
>> >> ds_cstr(&dnat_action),
>> >>> -                              NULL, meter, &ctx->lb->nlb->header_,
>> >>> -                              lflow_ref);
>> >>> +    ovn_lflow_add_with_hint__(ctx->lflows, od, S_ROUTER_IN_DNAT,
>> >>> +                              ctx->prio, ds_cstr(ctx->new_match),
>> >>> +                              ctx->new_action[type], NULL, meter,
>> >>> +                              &ctx->lb->nlb->header_, lflow_ref);
>> >>>
>> >>>      ds_truncate(ctx->new_match, new_match_len);
>> >>>
>> >>> -    ds_destroy(&dnat_action);
>> >>>      if (vector_is_empty(&ctx->lb_vip->backends)) {
>> >>>          return;
>> >>>      }
>> >>> @@ -12500,13 +12628,6 @@ build_distr_lrouter_nat_flows_for_lb(struct
>> >> lrouter_nat_lb_flows_ctx *ctx,
>> >>>          break;
>> >>>      }
>> >>>
>> >>> -    /* undnat_action: Remove the ct action from the lr_out_undenat
>> NAT
>> >> rule.
>> >>> -     */
>> >>> -    if (stateless_nat) {
>> >>> -        ds_clear(&undnat_action);
>> >>> -        ds_put_format(&undnat_action, "next;");
>> >>> -    }
>> >>> -
>> >>>      /* We need to centralize the LB traffic to properly perform
>> >>>       * the undnat stage.
>> >>>       */
>> >>> @@ -12525,48 +12646,11 @@ build_distr_lrouter_nat_flows_for_lb(struct
>> >> lrouter_nat_lb_flows_ctx *ctx,
>> >>>      ds_put_format(ctx->undnat_match, ") && (inport == %s || outport
>> ==
>> >> %s)"
>> >>>                    " && is_chassis_resident(%s)", dgp->json_key,
>> >> dgp->json_key,
>> >>>                    dgp->cr_port->json_key);
>> >>> -    /* Use the LB protocol as matching criteria for out undnat and
>> snat
>> >> when
>> >>> -     * creating LBs with stateless NAT. */
>> >>> -    if (stateless_nat) {
>> >>> -        ds_put_format(ctx->undnat_match, " && %s", ctx->lb->proto);
>> >>> -    }
>> >>>      ovn_lflow_add_with_hint(ctx->lflows, od, S_ROUTER_OUT_UNDNAT,
>> 120,
>> >>>                              ds_cstr(ctx->undnat_match),
>> >>>                              ds_cstr(&undnat_action),
>> >> &ctx->lb->nlb->header_,
>> >>>                              lflow_ref);
>> >>>
>> >>> -    /* (NOTE) snat_action: Add a new rule lr_out_snat with LB VIP as
>> >> source
>> >>> -     * IP action to perform stateless NAT pipeline completely when
>> the
>> >>> -     * outgoing packet is redirected to a chassis that does not have
>> an
>> >>> -     * active conntrack entry. Otherwise, it will not be SNATed by
>> the
>> >>> -     * ct_lb action because it does not refer to a valid created
>> flow.
>> >> The
>> >>> -     * use case for responding to a packet in different chassis is
>> >> multipath
>> >>> -     * via ECMP. So, the LB lr_out_snat is created with a lower
>> >> priority than
>> >>> -     * the other router pipeline entries, in this case, if the packet
>> >> is not
>> >>> -     * SNATed by ct_lb (conntrack lost), it will be SNATed by the LB
>> >>> -     * stateless NAT rule. Also, SNAT is performed only when the
>> packet
>> >>> -     * matches the configured LB backend IPs, ports and protocols.
>> >> Otherwise,
>> >>> -     * the packet will be forwarded without SNAted interference.
>> >>> -     */
>> >>> -    if (stateless_nat) {
>> >>> -        if (ctx->lb_vip->port_str) {
>> >>> -            ds_put_format(&snat_action, "%s.src = %s; %s.src = %s;
>> >> next;",
>> >>> -                          ctx->lb_vip->address_family == AF_INET6 ?
>> >>> -                          "ip6" : "ip4",
>> >>> -                          ctx->lb_vip->vip_str, ctx->lb->proto,
>> >>> -                          ctx->lb_vip->port_str);
>> >>> -        } else {
>> >>> -            ds_put_format(&snat_action, "%s.src = %s; next;",
>> >>> -                          ctx->lb_vip->address_family == AF_INET6 ?
>> >>> -                          "ip6" : "ip4",
>> >>> -                          ctx->lb_vip->vip_str);
>> >>> -        }
>> >>> -        ovn_lflow_add_with_hint(ctx->lflows, od, S_ROUTER_OUT_SNAT,
>> 160,
>> >>> -                                ds_cstr(ctx->undnat_match),
>> >>> -                                ds_cstr(&snat_action),
>> >> &ctx->lb->nlb->header_,
>> >>> -                                lflow_ref);
>> >>> -    }
>> >>> -
>> >>>      ds_truncate(ctx->undnat_match, undnat_match_len);
>> >>>      ds_destroy(&undnat_action);
>> >>>      ds_destroy(&snat_action);
>> >>> @@ -12726,8 +12810,7 @@ build_lrouter_nat_flows_for_lb(
>> >>>       * lflow generation for them.
>> >>>       */
>> >>>      size_t index;
>> >>> -    bool use_stateless_nat = smap_get_bool(&lb->nlb->options,
>> >>> -                                           "use_stateless_nat",
>> false);
>> >>> +    bool use_stateless_nat = lb->use_stateless_nat;
>> >>>      DYNAMIC_BITMAP_FOR_EACH_1 (index, &lb_dps->nb_lr_map) {
>> >>>          struct ovn_datapath *od = vector_get(&lr_datapaths->dps,
>> index,
>> >>>                                               struct ovn_datapath *);
>> >>> @@ -12876,21 +12959,48 @@ build_lrouter_defrag_flows_for_lb(struct
>> >> ovn_lb_datapaths *lb_dps,
>> >>>      if (dynamic_bitmap_is_empty(&lb_dps->nb_lr_map)) {
>> >>>          return;
>> >>>      }
>> >>> +    struct ds action = DS_EMPTY_INITIALIZER;
>> >>>
>> >>>      for (size_t i = 0; i < lb_dps->lb->n_vips; i++) {
>> >>>          struct ovn_lb_vip *lb_vip = &lb_dps->lb->vips[i];
>> >>>          bool ipv6 = lb_vip->address_family == AF_INET6;
>> >>>          int prio = 100;
>> >>> -
>> >>> +        enum ovn_stage stage = S_ROUTER_IN_DEFRAG;
>> >>>          ds_clear(match);
>> >>>          ds_put_format(match, "ip && ip%c.dst == %s", ipv6 ? '6' :
>> '4',
>> >>>                        lb_vip->vip_str);
>> >>> -
>> >>> +        if (lb_dps->lb->use_stateless_nat) {
>> >>> +            stage = S_ROUTER_IN_CT_EXTRACT;
>> >>> +            prio = 120;
>> >>> +            if (vector_len(&lb_vip->backends) > 1) {
>> >>> +                ds_put_format(&action, REG_IDX_LB_STATELESS" =
>> >> select(");
>> >>> +                if (lb_dps->lb->selection_fields) {
>> >>> +                    ds_put_format(&action, "values=(");
>> >>> +                }
>> >>> +                for (size_t idx_backend = 0; idx_backend <
>> >>> +                     vector_len(&lb_vip->backends);
>> >>> +                     idx_backend++) {
>> >>> +                    ds_put_format(&action, "%"PRIuSIZE",",
>> idx_backend);
>> >>> +                }
>> >>> +                ds_truncate(&action, action.length - 1);
>> >>> +                if (lb_dps->lb->selection_fields) {
>> >>> +                    ds_put_format(&action, "); hash_fields=\"%s\"",
>> >>> +                                  lb_dps->lb->selection_fields);
>> >>> +                }
>> >>> +                ds_put_format(&action,");");
>> >>> +            } else {
>> >>> +                ds_put_format(&action, "next;");
>> >>> +            }
>> >>
>> >> Maybe extract this part into a function for stateless-nat LBs like
>> >> we do with the other functions that build logical flows for such
>> >> load balancers?
>> >>
>> >> I agree.
>> >
>> >
>> >>> +        } else {
>> >>> +            ds_put_format(&action, "ct_dnat;");
>> >>> +        }
>> >>>          ovn_lflow_add_with_dp_group(
>> >>>              lflows, lb_dps->nb_lr_map.map, ods_size(lr_datapaths),
>> >>> -            S_ROUTER_IN_DEFRAG, prio, ds_cstr(match), "ct_dnat;",
>> >>> +            stage, prio, ds_cstr(match), ds_cstr(&action),
>> >>>              &lb_dps->lb->nlb->header_, lb_dps->lflow_ref);
>> >>> +        ds_clear(&action);
>> >>>      }
>> >>> +    ds_destroy(&action);
>> >>>  }
>> >>>
>> >>>  static void
>> >>> diff --git a/tests/multinode.at b/tests/multinode.at
>> >>> index a2c8e3f55..c4ec71481 100644
>> >>> --- a/tests/multinode.at
>> >>> +++ b/tests/multinode.at
>> >>> @@ -1643,6 +1643,7 @@ check multinode_nbctl ls-lb-add sw0 lb0
>> >>>
>> >>>  # Set use_stateless_nat to true
>> >>>  check multinode_nbctl set load_balancer lb0
>> >> options:use_stateless_nat=true
>> >>> +check multinode_nbctl set load_balancer lb0
>> >> selection_fields="ip_src,tp_src,ip_dst,tp_dst"
>> >>>
>> >>>  # Start backend http services
>> >>>  M_NS_DAEMONIZE([ovn-chassis-1], [sw0p1], [python3 -m http.server
>> --bind
>> >> 10.0.1.3 80 >/dev/null 2>&1], [http1.pid])
>> >>> @@ -1674,14 +1675,12 @@ m_as ovn-gw-2 ovs-appctl dpctl/flush-conntrack
>> >>>  M_NS_EXEC([ovn-chassis-3], [publicp1], [sh -c 'curl -v
>> 172.16.0.100:80
>> >> --retry 0 --connect-timeout 1 --max-time 1 --local-port 59001'])
>> >>>  OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-1 ovs-appctl dpctl/dump-conntrack |
>> >> M_FORMAT_CT(20.0.1.3) | \
>> >>>  grep tcp | sed -E -e 's/10.0.1.3|10.0.1.4/<cleared>/g' | sort], [0],
>> >> [dnl
>> >>>
>> >>
>> -tcp,orig=(src=20.0.1.3,dst=<cleared>,sport=59001,dport=80),reply=(src=<cleared>,dst=20.0.1.3,sport=80,dport=59001),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> >>>
>> >>
>> tcp,orig=(src=20.0.1.3,dst=<cleared>,sport=59001,dport=80),reply=(src=<cleared>,dst=20.0.1.3,sport=80,dport=59001),zone=<cleared>,protoinfo=(state=<cleared>)
>> >>>  ])
>> >>>
>> >>>  M_NS_EXEC([ovn-chassis-4], [publicp2], [sh -c 'curl -v
>> 172.16.0.100:80
>> >> --retry 0 --connect-timeout 1 --max-time 1 --local-port 59000'])
>> >>>  OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-2 ovs-appctl dpctl/dump-conntrack |
>> >> M_FORMAT_CT(30.0.1.3) | \
>> >>>  grep tcp | sed -E -e 's/10.0.1.3|10.0.1.4/<cleared>/g' | sort], [0],
>> >> [dnl
>> >>>
>> >>
>> -tcp,orig=(src=30.0.1.3,dst=<cleared>,sport=59000,dport=80),reply=(src=<cleared>,dst=30.0.1.3,sport=80,dport=59000),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> >>>
>> >>
>> tcp,orig=(src=30.0.1.3,dst=<cleared>,sport=59000,dport=80),reply=(src=<cleared>,dst=30.0.1.3,sport=80,dport=59000),zone=<cleared>,protoinfo=(state=<cleared>)
>> >>>  ])
>> >>>
>> >>> @@ -1712,7 +1711,6 @@ Connected to 172.16.0.100 (172.16.0.100) port 80
>> >>>  # Check if we have only one backend for the same connection - orig +
>> >> dest ports
>> >>>  OVS_WAIT_FOR_OUTPUT([echo -e $gw1_ct | M_FORMAT_CT(20.0.1.3) | \
>> >>>  grep tcp | sed -E -e 's/10.0.1.3|10.0.1.4/<cleared>/g' | sort], [0],
>> >> [dnl
>> >>>
>> >>
>> -tcp,orig=(src=20.0.1.3,dst=<cleared>,sport=59004,dport=80),reply=(src=<cleared>,dst=20.0.1.3,sport=80,dport=59004),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> >>>
>> >>
>> tcp,orig=(src=20.0.1.3,dst=<cleared>,sport=59004,dport=80),reply=(src=<cleared>,dst=20.0.1.3,sport=80,dport=59004),zone=<cleared>,protoinfo=(state=<cleared>)
>> >>>  ])
>> >>>
>> >>> @@ -1778,7 +1776,6 @@ Connected to 172.16.0.100 (172.16.0.100) port 80
>> >>>  # Check if we have only one backend for the same connection - orig +
>> >> dest ports
>> >>>  OVS_WAIT_FOR_OUTPUT([echo -e $gw1_ct | M_FORMAT_CT(20.0.1.3) | \
>> >>>  grep tcp | sed -E -e 's/10.0.1.3|10.0.1.4/<cleared>/g' | sort], [0],
>> >> [dnl
>> >>>
>> >>
>> -tcp,orig=(src=20.0.1.3,dst=<cleared>,sport=59005,dport=80),reply=(src=<cleared>,dst=20.0.1.3,sport=80,dport=59005),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> >>>
>> >>
>> tcp,orig=(src=20.0.1.3,dst=<cleared>,sport=59005,dport=80),reply=(src=<cleared>,dst=20.0.1.3,sport=80,dport=59005),zone=<cleared>,protoinfo=(state=<cleared>)
>> >>>  ])
>> >>>
>> >>> @@ -1834,7 +1831,6 @@ Connected to 172.16.0.100 (172.16.0.100) port 80
>> >>>  # Check if we have only one backend for the same connection - orig +
>> >> dest ports
>> >>>  OVS_WAIT_FOR_OUTPUT([echo -e $gw2_ct | M_FORMAT_CT(30.0.1.3) | \
>> >>>  grep tcp | sed -E -e 's/10.0.1.3|10.0.1.4/<cleared>/g' | sort], [0],
>> >> [dnl
>> >>>
>> >>
>> -tcp,orig=(src=30.0.1.3,dst=<cleared>,sport=59006,dport=80),reply=(src=<cleared>,dst=30.0.1.3,sport=80,dport=59006),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> >>>
>> >>
>> tcp,orig=(src=30.0.1.3,dst=<cleared>,sport=59006,dport=80),reply=(src=<cleared>,dst=30.0.1.3,sport=80,dport=59006),zone=<cleared>,protoinfo=(state=<cleared>)
>> >>>  ])
>> >>>
>> >>> @@ -1900,7 +1896,6 @@ Connected to 172.16.0.100 (172.16.0.100) port 80
>> >>>  # Check if we have only one backend for the same connection - orig +
>> >> dest ports
>> >>>  OVS_WAIT_FOR_OUTPUT([echo -e $gw2_ct | M_FORMAT_CT(30.0.1.3) | \
>> >>>  grep tcp | sed -E -e 's/10.0.1.3|10.0.1.4/<cleared>/g' | sort], [0],
>> >> [dnl
>> >>>
>> >>
>> -tcp,orig=(src=30.0.1.3,dst=<cleared>,sport=59007,dport=80),reply=(src=<cleared>,dst=30.0.1.3,sport=80,dport=59007),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> >>>
>> >>
>> tcp,orig=(src=30.0.1.3,dst=<cleared>,sport=59007,dport=80),reply=(src=<cleared>,dst=30.0.1.3,sport=80,dport=59007),zone=<cleared>,protoinfo=(state=<cleared>)
>> >>>  ])
>> >>>
>> >>> @@ -1987,7 +1982,6 @@ M_NS_EXEC([ovn-chassis-3], [publicp1], [sh -c
>> >> 'curl -v -O 172.16.0.100:9000/down
>> >>>  OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-1 ovs-appctl dpctl/dump-conntrack |
>> >> M_FORMAT_CT(20.0.1.3) | \
>> >>>  grep tcp | sed -E -e 's/10.0.1.3|10.0.1.4/<cleared>/g' | sort], [0],
>> >> [dnl
>> >>>
>> >>
>> tcp,orig=(src=20.0.1.3,dst=<cleared>,sport=59008,dport=80),reply=(src=<cleared>,dst=20.0.1.3,sport=80,dport=59008),zone=<cleared>,protoinfo=(state=<cleared>)
>> >>>
>> >>
>> -tcp,orig=(src=20.0.1.3,dst=<cleared>,sport=59008,dport=9000),reply=(src=<cleared>,dst=20.0.1.3,sport=80,dport=59008),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> >>>  ])
>> >>>
>> >>>  OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-3 cat -v curl.out |
>> >> M_FORMAT_CURL([172.16.0.100], [9000])], [0], [dnl
>> >>> @@ -2003,7 +1997,6 @@ M_NS_EXEC([ovn-chassis-4], [publicp2], [sh -c
>> >> 'curl -v -O 172.16.0.100:9000/down
>> >>>  OVS_WAIT_FOR_OUTPUT([m_as ovn-gw-2 ovs-appctl dpctl/dump-conntrack |
>> >> M_FORMAT_CT(30.0.1.3) | \
>> >>>  grep tcp | sed -E -e 's/10.0.1.3|10.0.1.4/<cleared>/g' | sort], [0],
>> >> [dnl
>> >>>
>> >>
>> tcp,orig=(src=30.0.1.3,dst=<cleared>,sport=59008,dport=80),reply=(src=<cleared>,dst=30.0.1.3,sport=80,dport=59008),zone=<cleared>,protoinfo=(state=<cleared>)
>> >>>
>> >>
>> -tcp,orig=(src=30.0.1.3,dst=<cleared>,sport=59008,dport=9000),reply=(src=<cleared>,dst=30.0.1.3,sport=80,dport=59008),zone=<cleared>,mark=<cleared>,protoinfo=(state=<cleared>)
>> >>>  ])
>> >>>
>> >>>  OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-3 cat -v curl.out |
>> >> M_FORMAT_CURL([172.16.0.100], [9000])], [0], [dnl
>> >>> @@ -2036,14 +2029,14 @@ done
>> >>>  #                                                   |
>> >>>  #
>> >>  +.............................|.............................+
>> >>>  #                     |
>> >>            |
>> >>> -#      DGP publicp3 (ovn-gw-3) (20.0.2.3/24)                     DGP
>> >> publicp4 (ovn-gw-4) (20.0.2.4/24)
>> >>> +#      DGP publicp3 (ovn-gw-3) (20.0.1.2/24)                     DGP
>> >> publicp4 (ovn-gw-4) (20.0.2.4/24)
>> >>>  #                     |
>> >>            |
>> >>>  #
>> >>  +.............................+.............................+
>> >>> -#                                                   |
>> >>> -#                                                   | (overlay)
>> >>> +#                     |
>> >>            |
>> >>> +#                     | (public_right)
>> >>  (public_left)|
>> >>>  #
>> >>  +.............................+.............................+
>> >>>  #                     |
>> >>            |
>> >>> -#      DGP public1 (ovn-gw-1) (20.0.2.1/24)                      DGP
>> >> public2 (ovn-gw-2) (20.0.2.2/24)
>> >>> +#      DGP public1 (ovn-gw-1) (20.0.1.1/24)                      DGP
>> >> public2 (ovn-gw-2) (20.0.2.2/24)
>> >>>  #                     |
>> >>            |
>> >>>  #
>> >>  +.............................+.............................+
>> >>>  #                                                   |
>> >>> @@ -2100,17 +2093,18 @@ check multinode_nbctl lsp-set-addresses
>> sw0-lr0
>> >> router
>> >>>  check multinode_nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
>> >>>
>> >>>  # create external connection for N/S traffic using multiple DGPs
>> >>> -check multinode_nbctl ls-add public
>> >>> +check multinode_nbctl ls-add public_right
>> >>> +check multinode_nbctl ls-add public_left
>> >>>
>> >>>  # create external connection for N/S traffic
>> >>>  # DGP public1
>> >>> -check multinode_nbctl lsp-add public ln-public-1
>> >>> +check multinode_nbctl lsp-add public_right ln-public-1
>> >>
>> >> This file needs a small rebase now that the patch adding the new
>> >> "lsp-add-localnet-port" nbctl helper has been merged to main.
>> >>
>> >> It should be relatively straightforward to rebase though.
>> >>
>> >>>  check multinode_nbctl lsp-set-type ln-public-1 localnet
>> >>>  check multinode_nbctl lsp-set-addresses ln-public-1 unknown
>> >>>  check multinode_nbctl lsp-set-options ln-public-1
>> network_name=public1
>> >>>
>> >>>  # DGP public2
>> >>> -check multinode_nbctl lsp-add public ln-public-2
>> >>> +check multinode_nbctl lsp-add public_left ln-public-2
>> >>>  check multinode_nbctl lsp-set-type ln-public-2 localnet
>> >>>  check multinode_nbctl lsp-set-addresses ln-public-2 unknown
>> >>>  check multinode_nbctl lsp-set-options ln-public-2
>> network_name=public2
>> >>> @@ -2121,8 +2115,8 @@ m_as ovn-gw-1 ovs-vsctl set open .
>> >> external-ids:ovn-bridge-mappings=public1:br-e
>> >>>  # Attach DGP public2 to GW-2 public2 (overlay connectivity)
>> >>>  m_as ovn-gw-2 ovs-vsctl set open .
>> >> external-ids:ovn-bridge-mappings=public2:br-ex
>> >>>
>> >>> -check multinode_nbctl lrp-add lr0 lr0-public-p1 40:54:00:00:00:01
>> >> 20.0.2.1/24 2000::1/64
>> >>> -check multinode_nbctl lsp-add public public-lr0-p1
>> >>> +check multinode_nbctl lrp-add lr0 lr0-public-p1 40:54:00:00:00:01
>> >> 20.0.1.1/24 2000::1/64
>> >>> +check multinode_nbctl lsp-add public_right public-lr0-p1
>> >>>  check multinode_nbctl lsp-set-type public-lr0-p1 router
>> >>>  check multinode_nbctl lsp-set-addresses public-lr0-p1 router
>> >>>  check multinode_nbctl lsp-set-options public-lr0-p1
>> >> router-port=lr0-public-p1
>> >>> @@ -2130,13 +2124,13 @@ check multinode_nbctl lrp-set-gateway-chassis
>> >> lr0-public-p1 ovn-gw-1 10
>> >>>
>> >>>  m_wait_for_ports_up
>> >>>
>> >>> -M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2
>> >> 20.0.2.1 | FORMAT_PING], \
>> >>> +M_NS_CHECK_EXEC([ovn-chassis-1], [sw0p1], [ping -q -c 3 -i 0.3 -w 2
>> >> 20.0.1.1 | FORMAT_PING], \
>> >>>  [0], [dnl
>> >>>  3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> >>>  ])
>> >>>
>> >>>  check multinode_nbctl lrp-add lr0 lr0-public-p2 40:54:00:00:00:02
>> >> 20.0.2.2/24 2000::2/64
>> >>> -check multinode_nbctl lsp-add public public-lr0-p2
>> >>> +check multinode_nbctl lsp-add public_left public-lr0-p2
>> >>>  check multinode_nbctl lsp-set-type public-lr0-p2 router
>> >>>  check multinode_nbctl lsp-set-addresses public-lr0-p2 router
>> >>>  check multinode_nbctl lsp-set-options public-lr0-p2
>> >> router-port=lr0-public-p2
>> >>> @@ -2157,7 +2151,7 @@ check multinode_nbctl lsp-set-options sw1-lr1
>> >> router-port=lr1-sw1
>> >>>
>> >>>  # create external connection for N/S traffic
>> >>>  # DGP public3
>> >>> -check multinode_nbctl lsp-add public ln-public-3
>> >>> +check multinode_nbctl lsp-add public_right ln-public-3
>> >>>  check multinode_nbctl lsp-set-type ln-public-3 localnet
>> >>>  check multinode_nbctl lsp-set-addresses ln-public-3 unknown
>> >>>  check multinode_nbctl lsp-set-options ln-public-3
>> network_name=public3
>> >>> @@ -2165,8 +2159,8 @@ check multinode_nbctl lsp-set-options
>> ln-public-3
>> >> network_name=public3
>> >>>  # Attach DGP public3 to GW-3 public3 (overlay connectivity)
>> >>>  m_as ovn-gw-3 ovs-vsctl set open .
>> >> external-ids:ovn-bridge-mappings=public3:br-ex
>> >>>
>> >>> -check multinode_nbctl lrp-add lr1 lr1-public-p3 40:54:00:00:00:03
>> >> 20.0.2.3/24 2000::3/64
>> >>> -check multinode_nbctl lsp-add public public-lr1-p3
>> >>> +check multinode_nbctl lrp-add lr1 lr1-public-p3 40:54:00:00:00:03
>> >> 20.0.1.2/24 2000::3/64
>> >>> +check multinode_nbctl lsp-add public_right public-lr1-p3
>> >>>  check multinode_nbctl lsp-set-type public-lr1-p3 router
>> >>>  check multinode_nbctl lsp-set-addresses public-lr1-p3 router
>> >>>  check multinode_nbctl lsp-set-options public-lr1-p3
>> >> router-port=lr1-public-p3
>> >>> @@ -2177,21 +2171,16 @@ M_NS_CHECK_EXEC([ovn-chassis-3], [sw1p1],
>> [ping
>> >> -q -c 3 -i 0.3 -w 2 40.0.2.1 | F
>> >>>  3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> >>>  ])
>> >>>
>> >>> -M_NS_CHECK_EXEC([ovn-chassis-3], [sw1p1], [ping -q -c 3 -i 0.3 -w 2
>> >> 20.0.2.3 | FORMAT_PING], \
>> >>> +M_NS_CHECK_EXEC([ovn-chassis-3], [sw1p1], [ping -q -c 3 -i 0.3 -w 2
>> >> 20.0.1.2 | FORMAT_PING], \
>> >>>  [0], [dnl
>> >>>  3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> >>>  ])
>> >>>
>> >>>  # Add a default route for multiple DGPs using ECMP - first step
>> >>> -check multinode_nbctl --ecmp lr-route-add lr0 0.0.0.0/0 20.0.2.3
>> >>> -check multinode_nbctl --ecmp lr-route-add lr1 0.0.0.0/0 20.0.2.1
>> >>> -
>> >>> -# Add SNAT rules using gateway-port
>> >>> -check multinode_nbctl --gateway-port lr0-public-p1 lr-nat-add lr0
>> snat
>> >> 20.0.2.1 10.0.2.0/24
>> >>> -check multinode_nbctl --gateway-port lr0-public-p2 lr-nat-add lr0
>> snat
>> >> 20.0.2.2 10.0.2.0/24
>> >>> -check multinode_nbctl --gateway-port lr1-public-p3 lr-nat-add lr1
>> snat
>> >> 20.0.2.3 40.0.2.0/24
>> >>> +check multinode_nbctl --ecmp lr-route-add lr0 0.0.0.0/0 20.0.1.2
>> >>> +check multinode_nbctl --ecmp lr-route-add lr1 0.0.0.0/0 20.0.1.1
>> >>>
>> >>> -M_NS_CHECK_EXEC([ovn-chassis-3], [sw1p1], [ping -q -c 3 -i 0.3 -w 2
>> >> 20.0.2.1 | FORMAT_PING], \
>> >>> +M_NS_CHECK_EXEC([ovn-chassis-3], [sw1p1], [ping -q -c 3 -i 0.3 -w 2
>> >> 20.0.1.1 | FORMAT_PING], \
>> >>>  [0], [dnl
>> >>>  3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> >>>  ])
>> >>> @@ -2203,7 +2192,7 @@ M_NS_CHECK_EXEC([ovn-chassis-3], [sw1p1], [ping
>> -q
>> >> -c 3 -i 0.3 -w 2 20.0.2.2 | F
>> >>>
>> >>>  # Configure the second DGP for the lr1
>> >>>  # DGP public4
>> >>> -check multinode_nbctl lsp-add public ln-public-4
>> >>> +check multinode_nbctl lsp-add public_left ln-public-4
>> >>>  check multinode_nbctl lsp-set-type ln-public-4 localnet
>> >>>  check multinode_nbctl lsp-set-addresses ln-public-4 unknown
>> >>>  check multinode_nbctl lsp-set-options ln-public-4
>> network_name=public4
>> >>> @@ -2212,7 +2201,7 @@ check multinode_nbctl lsp-set-options
>> ln-public-4
>> >> network_name=public4
>> >>>  m_as ovn-gw-4 ovs-vsctl set open .
>> >> external-ids:ovn-bridge-mappings=public4:br-ex
>> >>>
>> >>>  check multinode_nbctl lrp-add lr1 lr1-public-p4 40:54:00:00:00:04
>> >> 20.0.2.4/24 2000::4/64
>> >>> -check multinode_nbctl lsp-add public public-lr1-p4
>> >>> +check multinode_nbctl lsp-add public_left public-lr1-p4
>> >>>  check multinode_nbctl lsp-set-type public-lr1-p4 router
>> >>>  check multinode_nbctl lsp-set-addresses public-lr1-p4 router
>> >>>  check multinode_nbctl lsp-set-options public-lr1-p4
>> >> router-port=lr1-public-p4
>> >>> @@ -2223,9 +2212,6 @@ M_NS_CHECK_EXEC([ovn-chassis-3], [sw1p1], [ping
>> -q
>> >> -c 3 -i 0.3 -w 2 20.0.2.4 | F
>> >>>  3 packets transmitted, 3 received, 0% packet loss, time 0ms
>> >>>  ])
>> >>>
>> >>> -# Add SNAT rules using gateway-port
>> >>> -check multinode_nbctl --gateway-port lr1-public-p4 lr-nat-add lr1
>> snat
>> >> 20.0.2.4 40.0.2.0/24
>> >>> -
>> >>>  # Add a default route for multiple DGPs using ECMP - second step
>> >> (multipath)
>> >>>  check multinode_nbctl --ecmp lr-route-add lr0 0.0.0.0/0 20.0.2.4
>> >>>  check multinode_nbctl --ecmp lr-route-add lr1 0.0.0.0/0 20.0.2.2
>> >>> @@ -2359,36 +2345,77 @@ fi
>> >>>  # Set use_stateless_nat to true
>> >>>  # Now, if the traffic passes through both gateways (GW-1 and GW-2) it
>> >> will be forwarded successfully
>> >>>  check multinode_nbctl set load_balancer lb0
>> >> options:use_stateless_nat=true
>> >>> +check multinode_nbctl --wait=sb set load_balancer lb0
>> >> selection_fields="ip_src,tp_src,ip_dst,tp_dst"
>> >>>
>> >>>  # Check the flows again for the LB VIP - always needs to be
>> successful
>> >> regardless of the datapath (one or two gw chassis)
>> >>>  M_NS_EXEC([ovn-chassis-3], [sw1p1], [sh -c 'curl -v -O
>> >> 172.16.0.100:80/download_file --retry 0 --connect-timeout 1
>> --max-time 1
>> >> 2>curl.out'])
>> >>> +M_NS_EXEC([ovn-chassis-1], [sw0p1], [sh -c 'ss -nn
>> >connections.out'])
>> >>> +M_NS_EXEC([ovn-chassis-2], [sw0p2], [sh -c 'ss -nn
>> >connections.out'])
>> >>>
>> >>>  OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-3 cat -v curl.out |
>> >> M_FORMAT_CURL([172.16.0.100], [80])], [0], [dnl
>> >>>  Connected to 172.16.0.100 (172.16.0.100) port 80
>> >>>  200 OK
>> >>>  ])
>> >>>
>> >>> +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-1 cat connections.out | grep
>> >> "FIN-WAIT-2" | wc -l], [0], [dnl
>> >>> +0
>> >>> +])
>> >>> +
>> >>> +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-2 cat connections.out | grep
>> >> "FIN-WAIT-2" | wc -l], [0], [dnl
>> >>> +0
>> >>> +])
>> >>> +
>> >>>  M_NS_EXEC([ovn-chassis-3], [sw1p1], [sh -c 'curl -v -O
>> >> 172.16.0.100:80/download_file --retry 0 --connect-timeout 1
>> --max-time 1
>> >> 2>curl.out'])
>> >>> +M_NS_EXEC([ovn-chassis-1], [sw0p1], [sh -c 'ss -nn
>> >connections.out'])
>> >>> +M_NS_EXEC([ovn-chassis-2], [sw0p2], [sh -c 'ss -nn
>> >connections.out'])
>> >>>
>> >>>  OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-3 cat -v curl.out |
>> >> M_FORMAT_CURL([172.16.0.100], [80])], [0], [dnl
>> >>>  Connected to 172.16.0.100 (172.16.0.100) port 80
>> >>>  200 OK
>> >>>  ])
>> >>>
>> >>> +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-1 cat connections.out | grep
>> >> "FIN-WAIT-2" | wc -l], [0], [dnl
>> >>> +0
>> >>> +])
>> >>> +
>> >>> +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-2 cat connections.out | grep
>> >> "FIN-WAIT-2" | wc -l], [0], [dnl
>> >>> +0
>> >>> +])
>> >>> +
>> >>>  M_NS_EXEC([ovn-chassis-3], [sw1p1], [sh -c 'curl -v -O
>> >> 172.16.0.100:80/download_file --retry 0 --connect-timeout 1
>> --max-time 1
>> >> 2>curl.out'])
>> >>> +M_NS_EXEC([ovn-chassis-1], [sw0p1], [sh -c 'ss -nn
>> >connections.out'])
>> >>> +M_NS_EXEC([ovn-chassis-2], [sw0p2], [sh -c 'ss -nn
>> >connections.out'])
>> >>>
>> >>>  OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-3 cat -v curl.out |
>> >> M_FORMAT_CURL([172.16.0.100], [80])], [0], [dnl
>> >>>  Connected to 172.16.0.100 (172.16.0.100) port 80
>> >>>  200 OK
>> >>>  ])
>> >>>
>> >>> +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-1 cat connections.out | grep
>> >> "FIN-WAIT-2" | wc -l], [0], [dnl
>> >>> +0
>> >>> +])
>> >>> +
>> >>> +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-2 cat connections.out | grep
>> >> "FIN-WAIT-2" | wc -l], [0], [dnl
>> >>> +0
>> >>> +])
>> >>> +
>> >>>  M_NS_EXEC([ovn-chassis-3], [sw1p1], [sh -c 'curl -v -O
>> >> 172.16.0.100:80/download_file --retry 0 --connect-timeout 1
>> --max-time 1
>> >> 2>curl.out'])
>> >>> +M_NS_EXEC([ovn-chassis-1], [sw0p1], [sh -c 'ss -nn
>> >connections.out'])
>> >>> +M_NS_EXEC([ovn-chassis-2], [sw0p2], [sh -c 'ss -nn
>> >connections.out'])
>> >>>
>> >>>  OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-3 cat -v curl.out |
>> >> M_FORMAT_CURL([172.16.0.100], [80])], [0], [dnl
>> >>>  Connected to 172.16.0.100 (172.16.0.100) port 80
>> >>>  200 OK
>> >>>  ])
>> >>>
>> >>> +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-1 cat connections.out | grep
>> >> "FIN-WAIT-2" | wc -l], [0], [dnl
>> >>> +0
>> >>> +])
>> >>> +
>> >>> +OVS_WAIT_FOR_OUTPUT([m_as ovn-chassis-2 cat connections.out | grep
>> >> "FIN-WAIT-2" | wc -l], [0], [dnl
>> >>> +0
>> >>> +])
>> >>> +
>> >>>  # Direct backend traffic using the same LB ports needs to be dropped
>> >>>  M_NS_EXEC([ovn-chassis-3], [sw1p1], [sh -c 'curl -v -O
>> >> 10.0.2.3:80/download_file --retry 0 --connect-timeout 1 --max-time 1
>> >> 2>curl.out'])
>> >>>
>> >>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
>> >>> index fd572a023..5c9cc5cca 100644
>> >>> --- a/tests/ovn-northd.at
>> >>> +++ b/tests/ovn-northd.at
>> >>> @@ -14533,14 +14533,66 @@ ovn-sbctl dump-flows lr1 > lr1flows
>> >>>  AT_CAPTURE_FILE([lr1flows])
>> >>>
>> >>>  # Check stateless NAT rules for load balancer with multiple DGP
>> >>> -# 1. Check if the backend IPs are in the ipX.dst action
>> >>> +# 1. Check if the reg1[0..15] will select one of backends
>> >>> +AT_CHECK([grep "lr_in_ct_extract" lr1flows | ovn_strip_lflows | grep
>> >> "30.0.0.1"], [0], [dnl
>> >>> +  table=??(lr_in_ct_extract   ), priority=120  , match=(ip && ip4.dst
>> >> == 30.0.0.1), action=(reg1[[0..15]] = select(0,1,2);)
>> >>> +])
>> >>> +
>> >>> +# 2. Check if the backend IPs are in the ipX.dst action
>> >>>  AT_CHECK([grep "lr_in_dnat" lr1flows | ovn_strip_lflows | grep
>> >> "30.0.0.1"], [0], [dnl
>> >>> -  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> >> !ct.rel && ip4 && ip4.dst == 30.0.0.1 &&
>> >> is_chassis_resident("cr-lr1-ts1")), action=(ip4.dst = 172.16.0.103;
>> >> ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
>> >>> -  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> >> !ct.rel && ip4 && ip4.dst == 30.0.0.1 &&
>> >> is_chassis_resident("cr-lr1-ts2")), action=(ip4.dst = 172.16.0.103;
>> >> ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
>> >>> -  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> >> !ct.rel && ip4 && ip4.dst == 30.0.0.1 &&
>> >> is_chassis_resident("cr-lr1_public")), action=(ip4.dst = 172.16.0.103;
>> >> ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip4 && ip4.dst == 30.0.0.1
>> &&
>> >> reg1[[0..15]] == 0), action=(ip4.dst = 172.16.0.103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip4 && ip4.dst == 30.0.0.1
>> &&
>> >> reg1[[0..15]] == 1), action=(ip4.dst = 172.16.0.102; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip4 && ip4.dst == 30.0.0.1
>> &&
>> >> reg1[[0..15]] == 2), action=(ip4.dst = 172.16.0.101; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip4 && ip4.dst == 30.0.0.1
>> &&
>> >> reg1[[0..15]] == 0), action=(ip4.dst = 172.16.0.103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip4 && ip4.dst == 30.0.0.1
>> &&
>> >> reg1[[0..15]] == 1), action=(ip4.dst = 172.16.0.102; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip4 && ip4.dst == 30.0.0.1
>> &&
>> >> reg1[[0..15]] == 2), action=(ip4.dst = 172.16.0.101; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip4 && ip4.dst ==
>> 30.0.0.1
>> >> && reg1[[0..15]] == 0), action=(ip4.dst = 172.16.0.103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip4 && ip4.dst ==
>> 30.0.0.1
>> >> && reg1[[0..15]] == 1), action=(ip4.dst = 172.16.0.102; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip4 && ip4.dst ==
>> 30.0.0.1
>> >> && reg1[[0..15]] == 2), action=(ip4.dst = 172.16.0.101; next;)
>> >>> +])
>> >>> +
>> >>> +# 3. Check if the DGP ports are in the match with action next
>> >>> +AT_CHECK([grep "lr_out_undnat" lr1flows | ovn_strip_lflows], [0],
>> [dnl
>> >>> +  table=??(lr_out_undnat      ), priority=0    , match=(1),
>> >> action=(next;)
>> >>> +  table=??(lr_out_undnat      ), priority=120  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src ==
>> >> 172.16.0.101)) && (inport == "lr1-ts1" || outport == "lr1-ts1") &&
>> >> is_chassis_resident("cr-lr1-ts1") && tcp), action=(next;)
>> >>> +  table=??(lr_out_undnat      ), priority=120  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src ==
>> >> 172.16.0.101)) && (inport == "lr1-ts2" || outport == "lr1-ts2") &&
>> >> is_chassis_resident("cr-lr1-ts2") && tcp), action=(next;)
>> >>> +  table=??(lr_out_undnat      ), priority=120  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src ==
>> >> 172.16.0.101)) && (inport == "lr1_public" || outport == "lr1_public")
>> &&
>> >> is_chassis_resident("cr-lr1_public") && tcp), action=(next;)
>> >>>  ])
>> >>>
>> >>> -# 2. Check if the DGP ports are in the match with action next
>> >>> +# 4. Check if the VIP IP is in the ipX.src action
>> >>> +AT_CHECK([grep "lr_out_snat" lr1flows | 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=160  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src ==
>> >> 172.16.0.101)) && (inport == "lr1-ts1" || outport == "lr1-ts1") &&
>> >> is_chassis_resident("cr-lr1-ts1") && tcp), action=(ip4.src = 30.0.0.1;
>> >> next;)
>> >>> +  table=??(lr_out_snat        ), priority=160  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src ==
>> >> 172.16.0.101)) && (inport == "lr1-ts2" || outport == "lr1-ts2") &&
>> >> is_chassis_resident("cr-lr1-ts2") && tcp), action=(ip4.src = 30.0.0.1;
>> >> next;)
>> >>> +  table=??(lr_out_snat        ), priority=160  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src ==
>> >> 172.16.0.101)) && (inport == "lr1_public" || outport == "lr1_public")
>> &&
>> >> is_chassis_resident("cr-lr1_public") && tcp), action=(ip4.src =
>> 30.0.0.1;
>> >> next;)
>> >>> +])
>> >>> +
>> >>> +# Set selection fields
>> >>> +check ovn-nbctl --wait=sb set load_balancer lb1
>> >> selection_fields="ip_src,tp_src,ip_dst,tp_dst"
>> >>> +
>> >>> +ovn-sbctl dump-flows lr1 > lr1flows
>> >>> +AT_CAPTURE_FILE([lr1flows])
>> >>> +
>> >>> +# 1. Check if the reg1[0..15] will select one of backends using
>> >> hash_fields
>> >>> +AT_CHECK([grep "lr_in_ct_extract" lr1flows | ovn_strip_lflows | grep
>> >> "30.0.0.1"], [0], [dnl
>> >>> +  table=??(lr_in_ct_extract   ), priority=120  , match=(ip && ip4.dst
>> >> == 30.0.0.1), action=(reg1[[0..15]] = select(values=(0,1,2);
>> >> hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
>> >>> +])
>> >>> +
>> >>> +# 2. Check if the backend IPs are in the ipX.dst action
>> >>> +AT_CHECK([grep "lr_in_dnat" lr1flows | ovn_strip_lflows | grep
>> >> "30.0.0.1"], [0], [dnl
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip4 && ip4.dst == 30.0.0.1
>> &&
>> >> reg1[[0..15]] == 0), action=(ip4.dst = 172.16.0.103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip4 && ip4.dst == 30.0.0.1
>> &&
>> >> reg1[[0..15]] == 1), action=(ip4.dst = 172.16.0.102; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip4 && ip4.dst == 30.0.0.1
>> &&
>> >> reg1[[0..15]] == 2), action=(ip4.dst = 172.16.0.101; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip4 && ip4.dst == 30.0.0.1
>> &&
>> >> reg1[[0..15]] == 0), action=(ip4.dst = 172.16.0.103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip4 && ip4.dst == 30.0.0.1
>> &&
>> >> reg1[[0..15]] == 1), action=(ip4.dst = 172.16.0.102; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip4 && ip4.dst == 30.0.0.1
>> &&
>> >> reg1[[0..15]] == 2), action=(ip4.dst = 172.16.0.101; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip4 && ip4.dst ==
>> 30.0.0.1
>> >> && reg1[[0..15]] == 0), action=(ip4.dst = 172.16.0.103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip4 && ip4.dst ==
>> 30.0.0.1
>> >> && reg1[[0..15]] == 1), action=(ip4.dst = 172.16.0.102; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip4 && ip4.dst ==
>> 30.0.0.1
>> >> && reg1[[0..15]] == 2), action=(ip4.dst = 172.16.0.101; next;)
>> >>> +])
>> >>> +
>> >>> +# 3. Check if the DGP ports are in the match with action next
>> >>>  AT_CHECK([grep "lr_out_undnat" lr1flows | ovn_strip_lflows], [0],
>> [dnl
>> >>>    table=??(lr_out_undnat      ), priority=0    , match=(1),
>> >> action=(next;)
>> >>>    table=??(lr_out_undnat      ), priority=120  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src ==
>> >> 172.16.0.101)) && (inport == "lr1-ts1" || outport == "lr1-ts1") &&
>> >> is_chassis_resident("cr-lr1-ts1") && tcp), action=(next;)
>> >>> @@ -14548,7 +14600,7 @@ AT_CHECK([grep "lr_out_undnat" lr1flows |
>> >> ovn_strip_lflows], [0], [dnl
>> >>>    table=??(lr_out_undnat      ), priority=120  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src ==
>> >> 172.16.0.101)) && (inport == "lr1_public" || outport == "lr1_public")
>> &&
>> >> is_chassis_resident("cr-lr1_public") && tcp), action=(next;)
>> >>>  ])
>> >>>
>> >>> -# 3. Check if the VIP IP is in the ipX.src action
>> >>> +# 4. Check if the VIP IP is in the ipX.src action
>> >>>  AT_CHECK([grep "lr_out_snat" lr1flows | 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;)
>> >>> @@ -14557,6 +14609,51 @@ AT_CHECK([grep "lr_out_snat" lr1flows |
>> >> ovn_strip_lflows], [0], [dnl
>> >>>    table=??(lr_out_snat        ), priority=160  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103) || (ip4.src == 172.16.0.102) || (ip4.src ==
>> >> 172.16.0.101)) && (inport == "lr1_public" || outport == "lr1_public")
>> &&
>> >> is_chassis_resident("cr-lr1_public") && tcp), action=(ip4.src =
>> 30.0.0.1;
>> >> next;)
>> >>>  ])
>> >>>
>> >>> +# Delete LB and create with one backend
>> >>> +check ovn-nbctl --wait=sb lb-del lb1
>> >>> +check ovn-nbctl --wait=sb lb-add lb1 "30.0.0.1" "172.16.0.103"
>> >>> +
>> >>> +# Set use_stateless_nat to true
>> >>> +check ovn-nbctl --wait=sb set load_balancer lb1
>> >> options:use_stateless_nat=true
>> >>> +
>> >>> +# Associate load balancer to s1
>> >>> +check ovn-nbctl ls-lb-add s1 lb1
>> >>> +check ovn-nbctl lr-lb-add lr1 lb1
>> >>> +check ovn-nbctl --wait=sb sync
>> >>> +
>> >>> +ovn-sbctl dump-flows lr1 > lr1flows
>> >>> +AT_CAPTURE_FILE([lr1flows])
>> >>> +
>> >>> +# 1. Check if the reg1[0..15] will select one of backends using
>> >> hash_fields
>> >>> +AT_CHECK([grep "lr_in_ct_extract" lr1flows | ovn_strip_lflows | grep
>> >> "30.0.0.1"], [0], [dnl
>> >>> +  table=??(lr_in_ct_extract   ), priority=120  , match=(ip && ip4.dst
>> >> == 30.0.0.1), action=(next;)
>> >>> +])
>> >>> +
>> >>> +# 2. Check if the backend IPs are in the ipX.dst action
>> >>> +AT_CHECK([grep "lr_in_dnat" lr1flows | ovn_strip_lflows | grep
>> >> "30.0.0.1"], [0], [dnl
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip4 && ip4.dst ==
>> 30.0.0.1),
>> >> action=(ip4.dst = 172.16.0.103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip4 && ip4.dst ==
>> 30.0.0.1),
>> >> action=(ip4.dst = 172.16.0.103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip4 && ip4.dst ==
>> 30.0.0.1),
>> >> action=(ip4.dst = 172.16.0.103; next;)
>> >>> +])
>> >>> +
>> >>> +# 3. Check if the DGP ports are in the match with action next
>> >>> +AT_CHECK([grep "lr_out_undnat" lr1flows | ovn_strip_lflows], [0],
>> [dnl
>> >>> +  table=??(lr_out_undnat      ), priority=0    , match=(1),
>> >> action=(next;)
>> >>> +  table=??(lr_out_undnat      ), priority=120  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103)) && (inport == "lr1-ts1" || outport ==
>> >> "lr1-ts1") && is_chassis_resident("cr-lr1-ts1") && tcp), action=(next;)
>> >>> +  table=??(lr_out_undnat      ), priority=120  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103)) && (inport == "lr1-ts2" || outport ==
>> >> "lr1-ts2") && is_chassis_resident("cr-lr1-ts2") && tcp), action=(next;)
>> >>> +  table=??(lr_out_undnat      ), priority=120  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103)) && (inport == "lr1_public" || outport ==
>> >> "lr1_public") && is_chassis_resident("cr-lr1_public") && tcp),
>> >> action=(next;)
>> >>> +])
>> >>> +
>> >>> +# 4. Check if the VIP IP is in the ipX.src action
>> >>> +AT_CHECK([grep "lr_out_snat" lr1flows | 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=160  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103)) && (inport == "lr1-ts1" || outport ==
>> >> "lr1-ts1") && is_chassis_resident("cr-lr1-ts1") && tcp),
>> action=(ip4.src =
>> >> 30.0.0.1; next;)
>> >>> +  table=??(lr_out_snat        ), priority=160  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103)) && (inport == "lr1-ts2" || outport ==
>> >> "lr1-ts2") && is_chassis_resident("cr-lr1-ts2") && tcp),
>> action=(ip4.src =
>> >> 30.0.0.1; next;)
>> >>> +  table=??(lr_out_snat        ), priority=160  , match=(ip4 &&
>> >> ((ip4.src == 172.16.0.103)) && (inport == "lr1_public" || outport ==
>> >> "lr1_public") && is_chassis_resident("cr-lr1_public") && tcp),
>> >> action=(ip4.src = 30.0.0.1; next;)
>> >>> +])
>> >>> +
>> >>> +
>> >>>  AT_CLEANUP
>> >>>  ])
>> >>>
>> >>> @@ -14647,14 +14744,67 @@ ovn-sbctl dump-flows lr1 > lr1flows
>> >>>  AT_CAPTURE_FILE([lr1flows])
>> >>>
>> >>>  # Check stateless NAT rules for load balancer with multiple DGP
>> >>> -# 1. Check if the backend IPs are in the ipX.dst action
>> >>> +# 1. Check if the reg1[0..15] will select one of backends
>> >>> +AT_CHECK([grep "lr_in_ct_extract" lr1flows | ovn_strip_lflows | grep
>> >> "2001:db8:cccc::1"], [0], [dnl
>> >>> +  table=??(lr_in_ct_extract   ), priority=120  , match=(ip && ip6.dst
>> >> == 2001:db8:cccc::1), action=(reg1[[0..15]] = select(0,1,2);)
>> >>> +])
>> >>> +
>> >>> +# 2. Check if the backend IPs are in the ipX.dst action
>> >>>  AT_CHECK([grep "lr_in_dnat" lr1flows | ovn_strip_lflows | grep
>> >> "2001:db8:cccc::1"], [0], [dnl
>> >>> -  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> >> !ct.rel && ip6 && ip6.dst == 2001:db8:cccc::1 &&
>> >> is_chassis_resident("cr-lr1-ts1")), action=(ip6.dst =
>> 2001:db8:aaaa:3::103;
>> >>
>> ct_lb_mark(backends=2001:db8:aaaa:3::103,2001:db8:aaaa:3::102,2001:db8:aaaa:3::101);)
>> >>> -  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> >> !ct.rel && ip6 && ip6.dst == 2001:db8:cccc::1 &&
>> >> is_chassis_resident("cr-lr1-ts2")), action=(ip6.dst =
>> 2001:db8:aaaa:3::103;
>> >>
>> ct_lb_mark(backends=2001:db8:aaaa:3::103,2001:db8:aaaa:3::102,2001:db8:aaaa:3::101);)
>> >>> -  table=??(lr_in_dnat         ), priority=110  , match=(ct.new &&
>> >> !ct.rel && ip6 && ip6.dst == 2001:db8:cccc::1 &&
>> >> is_chassis_resident("cr-lr1_public")), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::103;
>> >>
>> ct_lb_mark(backends=2001:db8:aaaa:3::103,2001:db8:aaaa:3::102,2001:db8:aaaa:3::101);)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 0), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 1), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::102; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 2), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::101; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 0), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 1), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::102; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 2), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::101; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 0), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 1), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::102; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 2), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::101; next;)
>> >>> +])
>> >>> +
>> >>> +# 3. Check if the DGP ports are in the match with action next
>> >>> +AT_CHECK([grep "lr_out_undnat" lr1flows | ovn_strip_lflows], [0],
>> [dnl
>> >>> +  table=??(lr_out_undnat      ), priority=0    , match=(1),
>> >> action=(next;)
>> >>> +  table=??(lr_out_undnat      ), priority=120  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> 2001:db8:aaaa:3::102) ||
>> >> (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1-ts1" || outport
>> ==
>> >> "lr1-ts1") && is_chassis_resident("cr-lr1-ts1") && tcp), action=(next;)
>> >>> +  table=??(lr_out_undnat      ), priority=120  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> 2001:db8:aaaa:3::102) ||
>> >> (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1-ts2" || outport
>> ==
>> >> "lr1-ts2") && is_chassis_resident("cr-lr1-ts2") && tcp), action=(next;)
>> >>> +  table=??(lr_out_undnat      ), priority=120  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> 2001:db8:aaaa:3::102) ||
>> >> (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1_public" ||
>> outport ==
>> >> "lr1_public") && is_chassis_resident("cr-lr1_public") && tcp),
>> >> action=(next;)
>> >>>  ])
>> >>>
>> >>> -# 2. Check if the DGP ports are in the match with action next
>> >>> +# 4. Check if the VIP IP is in the ipX.src action
>> >>> +AT_CHECK([grep "lr_out_snat" lr1flows | 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=160  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> 2001:db8:aaaa:3::102) ||
>> >> (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1-ts1" || outport
>> ==
>> >> "lr1-ts1") && is_chassis_resident("cr-lr1-ts1") && tcp),
>> action=(ip6.src =
>> >> 2001:db8:cccc::1; next;)
>> >>> +  table=??(lr_out_snat        ), priority=160  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> 2001:db8:aaaa:3::102) ||
>> >> (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1-ts2" || outport
>> ==
>> >> "lr1-ts2") && is_chassis_resident("cr-lr1-ts2") && tcp),
>> action=(ip6.src =
>> >> 2001:db8:cccc::1; next;)
>> >>> +  table=??(lr_out_snat        ), priority=160  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> 2001:db8:aaaa:3::102) ||
>> >> (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1_public" ||
>> outport ==
>> >> "lr1_public") && is_chassis_resident("cr-lr1_public") && tcp),
>> >> action=(ip6.src = 2001:db8:cccc::1; next;)
>> >>> +])
>> >>> +
>> >>> +# Set selection fields
>> >>> +check ovn-nbctl --wait=sb set load_balancer lb1
>> >> selection_fields="ip_src,tp_src,ip_dst,tp_dst"
>> >>> +
>> >>> +ovn-sbctl dump-flows lr1 > lr1flows
>> >>> +AT_CAPTURE_FILE([lr1flows])
>> >>> +
>> >>> +# Check stateless NAT rules for load balancer with multiple DGP
>> >>> +# 1. Check if the reg1[0..15] will select one of backends
>> >>> +AT_CHECK([grep "lr_in_ct_extract" lr1flows | ovn_strip_lflows | grep
>> >> "2001:db8:cccc::1"], [0], [dnl
>> >>> +  table=??(lr_in_ct_extract   ), priority=120  , match=(ip && ip6.dst
>> >> == 2001:db8:cccc::1), action=(reg1[[0..15]] = select(values=(0,1,2);
>> >> hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");)
>> >>> +])
>> >>> +
>> >>> +# 2. Check if the backend IPs are in the ipX.dst action
>> >>> +AT_CHECK([grep "lr_in_dnat" lr1flows | ovn_strip_lflows | grep
>> >> "2001:db8:cccc::1"], [0], [dnl
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 0), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 1), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::102; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 2), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::101; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 0), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 1), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::102; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 2), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::101; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 0), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 1), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::102; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1 && reg1[[0..15]] == 2), action=(ip6.dst =
>> >> 2001:db8:aaaa:3::101; next;)
>> >>> +])
>> >>> +
>> >>> +# 3. Check if the DGP ports are in the match with action next
>> >>>  AT_CHECK([grep "lr_out_undnat" lr1flows | ovn_strip_lflows], [0],
>> [dnl
>> >>>    table=??(lr_out_undnat      ), priority=0    , match=(1),
>> >> action=(next;)
>> >>>    table=??(lr_out_undnat      ), priority=120  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> 2001:db8:aaaa:3::102) ||
>> >> (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1-ts1" || outport
>> ==
>> >> "lr1-ts1") && is_chassis_resident("cr-lr1-ts1") && tcp), action=(next;)
>> >>> @@ -14662,7 +14812,7 @@ AT_CHECK([grep "lr_out_undnat" lr1flows |
>> >> ovn_strip_lflows], [0], [dnl
>> >>>    table=??(lr_out_undnat      ), priority=120  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> 2001:db8:aaaa:3::102) ||
>> >> (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1_public" ||
>> outport ==
>> >> "lr1_public") && is_chassis_resident("cr-lr1_public") && tcp),
>> >> action=(next;)
>> >>>  ])
>> >>>
>> >>> -# 3. Check if the VIP IP is in the ipX.src action
>> >>> +# 4. Check if the VIP IP is in the ipX.src action
>> >>>  AT_CHECK([grep "lr_out_snat" lr1flows | 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;)
>> >>> @@ -14671,6 +14821,50 @@ AT_CHECK([grep "lr_out_snat" lr1flows |
>> >> ovn_strip_lflows], [0], [dnl
>> >>>    table=??(lr_out_snat        ), priority=160  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103) || (ip6.src ==
>> 2001:db8:aaaa:3::102) ||
>> >> (ip6.src == 2001:db8:aaaa:3::101)) && (inport == "lr1_public" ||
>> outport ==
>> >> "lr1_public") && is_chassis_resident("cr-lr1_public") && tcp),
>> >> action=(ip6.src = 2001:db8:cccc::1; next;)
>> >>>  ])
>> >>>
>> >>> +# Delete LB and create with one backend
>> >>> +check ovn-nbctl --wait=sb lb-del lb1
>> >>> +check ovn-nbctl --wait=sb lb-add lb1 "2001:db8:cccc::1"
>> >> "2001:db8:aaaa:3::103"
>> >>> +
>> >>> +# Set use_stateless_nat to true
>> >>> +check ovn-nbctl --wait=sb set load_balancer lb1
>> >> options:use_stateless_nat=true
>> >>> +
>> >>> +# Associate load balancer to s1
>> >>> +check ovn-nbctl ls-lb-add s1 lb1
>> >>> +check ovn-nbctl lr-lb-add lr1 lb1
>> >>> +check ovn-nbctl --wait=sb sync
>> >>> +
>> >>> +ovn-sbctl dump-flows lr1 > lr1flows
>> >>> +AT_CAPTURE_FILE([lr1flows])
>> >>> +# Check stateless NAT rules for load balancer with multiple DGP
>> >>> +# 1. Check if the reg1[0..15] will select one of backends
>> >>> +AT_CHECK([grep "lr_in_ct_extract" lr1flows | ovn_strip_lflows | grep
>> >> "2001:db8:cccc::1"], [0], [dnl
>> >>> +  table=??(lr_in_ct_extract   ), priority=120  , match=(ip && ip6.dst
>> >> == 2001:db8:cccc::1), action=(next;)
>> >>> +])
>> >>> +
>> >>> +# 2. Check if the backend IPs are in the ipX.dst action
>> >>> +AT_CHECK([grep "lr_in_dnat" lr1flows | ovn_strip_lflows | grep
>> >> "2001:db8:cccc::1"], [0], [dnl
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts1") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1), action=(ip6.dst = 2001:db8:aaaa:3::103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1-ts2") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1), action=(ip6.dst = 2001:db8:aaaa:3::103; next;)
>> >>> +  table=??(lr_in_dnat         ), priority=110  ,
>> >> match=(is_chassis_resident("cr-lr1_public") && ip6 && ip6.dst ==
>> >> 2001:db8:cccc::1), action=(ip6.dst = 2001:db8:aaaa:3::103; next;)
>> >>> +])
>> >>> +
>> >>> +# 3. Check if the DGP ports are in the match with action next
>> >>> +AT_CHECK([grep "lr_out_undnat" lr1flows | ovn_strip_lflows], [0],
>> [dnl
>> >>> +  table=??(lr_out_undnat      ), priority=0    , match=(1),
>> >> action=(next;)
>> >>> +  table=??(lr_out_undnat      ), priority=120  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103)) && (inport == "lr1-ts1" || outport
>> ==
>> >> "lr1-ts1") && is_chassis_resident("cr-lr1-ts1") && tcp), action=(next;)
>> >>> +  table=??(lr_out_undnat      ), priority=120  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103)) && (inport == "lr1-ts2" || outport
>> ==
>> >> "lr1-ts2") && is_chassis_resident("cr-lr1-ts2") && tcp), action=(next;)
>> >>> +  table=??(lr_out_undnat      ), priority=120  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103)) && (inport == "lr1_public" ||
>> outport
>> >> == "lr1_public") && is_chassis_resident("cr-lr1_public") && tcp),
>> >> action=(next;)
>> >>> +])
>> >>> +
>> >>> +# 4. Check if the VIP IP is in the ipX.src action
>> >>> +AT_CHECK([grep "lr_out_snat" lr1flows | 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=160  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103)) && (inport == "lr1-ts1" || outport
>> ==
>> >> "lr1-ts1") && is_chassis_resident("cr-lr1-ts1") && tcp),
>> action=(ip6.src =
>> >> 2001:db8:cccc::1; next;)
>> >>> +  table=??(lr_out_snat        ), priority=160  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103)) && (inport == "lr1-ts2" || outport
>> ==
>> >> "lr1-ts2") && is_chassis_resident("cr-lr1-ts2") && tcp),
>> action=(ip6.src =
>> >> 2001:db8:cccc::1; next;)
>> >>> +  table=??(lr_out_snat        ), priority=160  , match=(ip6 &&
>> >> ((ip6.src == 2001:db8:aaaa:3::103)) && (inport == "lr1_public" ||
>> outport
>> >> == "lr1_public") && is_chassis_resident("cr-lr1_public") && tcp),
>> >> action=(ip6.src = 2001:db8:cccc::1; next;)
>> >>> +])
>> >>> +
>> >>>  AT_CLEANUP
>> >>>  ])
>> >>>
>> >>
>> >> Regards,
>> >> Dumitru
>> >>
>> >>
>> >>
>> > Regards,
>> > Lucas
>> >
>>
>> Thanks,
>> Dumitru
>>
>>

-- 




_‘Esta mensagem é direcionada apenas para os endereços constantes no 
cabeçalho inicial. Se você não está listado nos endereços constantes no 
cabeçalho, pedimos-lhe que desconsidere completamente o conteúdo dessa 
mensagem e cuja cópia, encaminhamento e/ou execução das ações citadas estão 
imediatamente anuladas e proibidas’._


* **‘Apesar do Magazine Luiza tomar 
todas as precauções razoáveis para assegurar que nenhum vírus esteja 
presente nesse e-mail, a empresa não poderá aceitar a responsabilidade por 
quaisquer perdas ou danos causados por esse e-mail ou por seus anexos’.*



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

Reply via email to