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]> --- northd/lb.c | 3 + northd/lb.h | 1 + northd/northd.c | 298 +++++++++++++++++++++++++++++++------------- tests/multinode.at | 99 +++++++++------ tests/ovn-northd.at | 217 ++++++++++++++++++++++++++++++-- 5 files changed, 481 insertions(+), 137 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 cdf12ec86..4defff1cc 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]" @@ -12439,6 +12440,155 @@ 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); + 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); + } + + ds_destroy(&new_match_stateless_nat); + ds_destroy(&new_action_stateless_nat); + + if (vector_is_empty(&ctx->lb_vip->backends)) { + return; + } + + size_t undnat_match_len = ctx->undnat_match->length; + struct ds snat_action = DS_EMPTY_INITIALIZER; + + + /* 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); + + /* 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); + + + /* undnat_action: Just follows the pipeline in the lr_out_undenat NAT rule. + */ + ovn_lflow_add_with_hint(ctx->lflows, od, S_ROUTER_OUT_UNDNAT, 120, + ds_cstr(ctx->undnat_match), + "next;", &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;", + ip_match, ctx->lb_vip->vip_str, ctx->lb->proto, + ctx->lb_vip->port_str); + } else { + ds_put_format(&snat_action, "%s.src = %s; next;", + ip_match, 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(&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, @@ -12447,66 +12597,39 @@ 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; } struct ds undnat_action = DS_EMPTY_INITIALIZER; - struct ds snat_action = DS_EMPTY_INITIALIZER; switch (type) { case LROUTER_NAT_LB_FLOW_FORCE_SNAT: @@ -12523,13 +12646,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. */ @@ -12546,53 +12662,16 @@ build_distr_lrouter_nat_flows_for_lb(struct lrouter_nat_lb_flows_ctx *ctx, 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); - /* 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); - } + " && is_chassis_resident(%s)", + dgp->json_key, dgp->json_key, + dgp->cr_port->json_key); 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); } static void @@ -12749,8 +12828,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 *); @@ -12883,13 +12961,45 @@ build_lswitch_flows_for_lb(struct ovn_lb_datapaths *lb_dps, build_lb_rules_for_stateless_acl(lflows, lb_dps); } +static void +build_lrouter_defrag_flows_for_lb_stateless(const struct ovn_northd_lb *lb, + const struct ovn_lb_vip *lb_vip, + struct ds *action) +{ + ovs_assert(lb && lb_vip && action); + + if (vector_len(&lb_vip->backends) > 1) { + ds_put_format(action, REG_IDX_LB_STATELESS" = select("); + if (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->selection_fields) { + ds_put_format(action, "); hash_fields=\"%s\"", + lb->selection_fields); + } + ds_put_format(action,");"); + } else { + ds_put_format(action, "next;"); + } +} + /* If there are any load balancing rules, we should send the packet to - * conntrack for defragmentation and tracking. This helps with two things. + * conntrack for defragmentation and tracking, unless LB is stateless. + * This helps with two things. * * 1. With tracking, we can send only new connections to pick a DNAT ip address * from a group. * 2. If there are L4 ports in load balancing rules, we need the * defragmentation to match on L4 ports. + * + * If load balancer is stateless, conntrack must not be used. + * Here, basicaly, it's calculated a hash to select the backend. */ static void build_lrouter_defrag_flows_for_lb(struct ovn_lb_datapaths *lb_dps, @@ -12900,21 +13010,31 @@ 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; + build_lrouter_defrag_flows_for_lb_stateless(lb_dps->lb, lb_vip, + &action); + } 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 2f74487c8..5b001994d 100644 --- a/tests/multinode.at +++ b/tests/multinode.at @@ -1514,6 +1514,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]) @@ -1545,14 +1546,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>) ]) @@ -1583,7 +1582,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>) ]) @@ -1649,7 +1647,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>) ]) @@ -1705,7 +1702,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>) ]) @@ -1771,7 +1767,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>) ]) @@ -1858,7 +1853,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 @@ -1874,7 +1868,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 @@ -1907,14 +1900,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) # | | # +.............................+.............................+ # | @@ -1968,14 +1961,15 @@ check multinode_nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.2.1/24 1000::a/ check multinode_nbctl lsp-add-router-port sw0 sw0-lr0 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-localnet-port public ln-public-1 public1 +check multinode_nbctl lsp-add-localnet-port public_right ln-public-1 public1 # DGP public2 -check multinode_nbctl lsp-add-localnet-port public ln-public-2 public2 +check multinode_nbctl lsp-add-localnet-port public_left ln-public-2 public2 # Attach DGP public1 to GW-1 public1 (overlay connectivity) m_as ovn-gw-1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=public1:br-ex @@ -1983,19 +1977,19 @@ 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-router-port public public-lr0-p1 lr0-public-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-router-port public_right public-lr0-p1 lr0-public-p1 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-router-port public public-lr0-p2 lr0-public-p2 +check multinode_nbctl lsp-add-router-port public_left public-lr0-p2 lr0-public-p2 check multinode_nbctl lrp-set-gateway-chassis lr0-public-p2 ovn-gw-2 10 M_NS_CHECK_EXEC([ovn-chassis-2], [sw0p2], [ping -q -c 3 -i 0.3 -w 2 20.0.2.2 | FORMAT_PING], \ @@ -2010,13 +2004,13 @@ check multinode_nbctl lsp-add-router-port sw1 sw1-lr1 lr1-sw1 # create external connection for N/S traffic # DGP public3 -check multinode_nbctl lsp-add-localnet-port public ln-public-3 public3 +check multinode_nbctl lsp-add-localnet-port public_left ln-public-3 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-router-port public public-lr1-p3 lr1-public-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-router-port public_left public-lr1-p3 lr1-public-p3 check multinode_nbctl lrp-set-gateway-chassis lr1-public-p3 ovn-gw-3 10 M_NS_CHECK_EXEC([ovn-chassis-3], [sw1p1], [ping -q -c 3 -i 0.3 -w 2 40.0.2.1 | FORMAT_PING], \ @@ -2024,21 +2018,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 ]) @@ -2050,13 +2039,13 @@ 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-localnet-port public ln-public-4 public4 +check multinode_nbctl lsp-add-localnet-port public_right ln-public-4 public4 # Attach DGP public4 to GW-2 public4 (overlay connectivity) 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-router-port public public-lr1-p4 lr1-public-p4 +check multinode_nbctl lsp-add-router-port public_right public-lr1-p4 lr1-public-p4 check multinode_nbctl lrp-set-gateway-chassis lr1-public-p4 ovn-gw-4 10 M_NS_CHECK_EXEC([ovn-chassis-3], [sw1p1], [ping -q -c 3 -i 0.3 -w 2 20.0.2.4 | FORMAT_PING], \ @@ -2064,9 +2053,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 @@ -2200,36 +2186,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 448bc66ae..1551df17f 100644 --- a/tests/ovn-northd.at +++ b/tests/ovn-northd.at @@ -14300,14 +14300,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;) @@ -14315,7 +14367,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;) @@ -14324,6 +14376,50 @@ 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 ]) @@ -14405,14 +14501,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;) @@ -14420,7 +14569,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;) @@ -14429,6 +14578,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 ]) -- 2.43.0 -- _'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
