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
