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 '&'.
> + 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?
> + 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);
> +
> + /* 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",
> + 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.
> + 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?
> + } 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
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev