Hi Numan, Thanks for the fix!
On 11/5/24 18:00, [email protected] wrote: > From: Numan Siddique <[email protected]> > > If we have a UDP load balancer - 10.0.0.10:80 = 10.0.0.3:8080, in order to > determine if the load balanced traffic needs to be hairpinned, the > vip - 10.0.0.10 and the vip port - 80 are stored in the registers before > the packet is load balanced using the below logical flow - > > table=6 (ls_in_pre_stateful ), priority=120 , > match=(reg0[2] == 1 && ip4.dst == 10.0.0.10 && tcp.dst == 80), > action=(reg1 = 10.0.0.10; reg2[0..15] = 80; ct_lb_mark;) > > These registers are used in the later stages to check if the load balanced > packet needs to be hairpinned or not. > > However, if the packet is fragmented we may not be able to match on the > L4 fields (tcp, udp or sctp dest port) and this breaks the hairpin > traffic. > > This patch addressed this issue by making use of ct_nw_dst/ct_ip6_dst and > ct_tp_dst conntrack fields to determine the hairpin load balanced > traffic. > You mentioned it in the code comments but it's probably worth mentioning in the commit log too: Relying on conntrack to reassemble packets is not exactly correct, it only accidentaly works with the kernel datapath. In our internal bug tracking system we have this issue to track this incorrect assumption: https://issues.redhat.com/browse/FDP-913 > In order to not break hardware offload on certain smart nics, care is taken > to match on these fields only for fragmented packets. > > Reported-at: https://issues.redhat.com/browse/FDP-905 > Fixes: 1139b655c996 ("Don't blindly save original dst IP and Port to avoid > megaflow unwildcarding.") > CC: Han Zhou <[email protected]> > Suggested-by: Dumitru Ceara <[email protected]> > Signed-off-by: Numan Siddique <[email protected]> > --- > controller/lflow.c | 3 + > controller/lflow.h | 4 + > controller/physical.c | 37 +++++++++ > include/ovn/actions.h | 14 +++- > include/ovn/logical-fields.h | 4 + > lib/actions.c | 138 ++++++++++++++++++++++++++++---- > northd/northd.c | 57 +++++++++++++- > ovn-sb.xml | 27 +++++++ > tests/ovn-macros.at | 3 + > tests/ovn-northd.at | 70 +++++++++------- > tests/ovn.at | 56 ++++++++++++- > tests/system-ovn-kmod.at | 149 +++++++++++++++++++++++++++++++++++ > tests/test-ovn.c | 3 + > utilities/ovn-trace.c | 6 ++ > 14 files changed, 520 insertions(+), 51 deletions(-) > > diff --git a/controller/lflow.c b/controller/lflow.c > index 13c3a0d73e..76b4952fd4 100644 > --- a/controller/lflow.c > +++ b/controller/lflow.c > @@ -886,6 +886,9 @@ add_matches_to_flow_table(const struct sbrec_logical_flow > *lflow, > .in_port_sec_ptable = OFTABLE_CHK_IN_PORT_SEC, > .out_port_sec_ptable = OFTABLE_CHK_OUT_PORT_SEC, > .mac_cache_use_table = OFTABLE_MAC_CACHE_USE, > + .ct_nw_dst_load_table = OFTABLE_CT_ORIG_NW_DST_LOAD, > + .ct_ip6_dst_load_table = OFTABLE_CT_ORIG_IP6_DST_LOAD, > + .ct_tp_dst_load_table = OFTABLE_CT_ORIG_TP_DST_LOAD, > .ctrl_meter_id = ctrl_meter_id, > .common_nat_ct_zone = get_common_nat_zone(ldp), > }; > diff --git a/controller/lflow.h b/controller/lflow.h > index e95a016501..58a12ee0fe 100644 > --- a/controller/lflow.h > +++ b/controller/lflow.h > @@ -95,6 +95,10 @@ struct uuid; > #define OFTABLE_CHK_LB_AFFINITY 78 > #define OFTABLE_MAC_CACHE_USE 79 > #define OFTABLE_CT_ZONE_LOOKUP 80 > +#define OFTABLE_CT_ORIG_NW_DST_LOAD 81 > +#define OFTABLE_CT_ORIG_IP6_DST_LOAD 82 > +#define OFTABLE_CT_ORIG_TP_DST_LOAD 83 > + > > struct lflow_ctx_in { > struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath; > diff --git a/controller/physical.c b/controller/physical.c > index 2aaa16cbd0..855936ba4e 100644 > --- a/controller/physical.c > +++ b/controller/physical.c > @@ -2770,5 +2770,42 @@ physical_run(struct physical_ctx *p_ctx, > */ > add_default_drop_flow(p_ctx, OFTABLE_LOG_TO_PHY, flow_table); > > + /* Table 81, 82 and 83 > + * Match on ct.trk and ct.dnat and store the ct_nw_dst, ct_ip6_dst and > + * ct_tp_dst in the registers. */ > + uint32_t ct_state = OVS_CS_F_TRACKED | OVS_CS_F_DST_NAT; I did some testing with this patch and these flows are actually only hit if the ct_state of the session is +est - that's fine. However, because ovn-northd generates flows that resubmit all IP fragments to these tables in the "in_lb" stage of the pipeline, before we could decide if this is part of a hairpin session or not (that happens later in "in_pre_hairpin"), it means that all IP fragments that are part of load balanced sessions will match on ct_state=+trk+dnat in the datapath - including those on sessions that are not hairpin. As you mentioned off-list yesterday, that breaks HW offload on certain hardware for fragmented load balanced traffic: https://github.com/ovn-org/ovn/commit/110e670 I experimented a bit and it seems to me that we can just avoid the ct_state=+trk+dnat match all toghether. I changed this to +trk+est (established is a prerequisite for ct_nw/tp_* fields): uint32_t ct_state = OVS_CS_F_TRACKED | OVS_CS_F_ESTABLISHED; and traffic seems to be natted properly for hairpin sessions with fragmented traffic too. > + match_init_catchall(&match); > + ofpbuf_clear(&ofpacts); > + > + /* Add the flow: > + * match = (ct.trk && ct.dnat), action = (reg8 = ct_tp_dst) > + * table = 83 > + */ > + match_set_ct_state_masked(&match, ct_state, ct_state); > + put_move(MFF_CT_TP_DST, 0, MFF_LOG_CT_ORIG_TP_DST_PORT, 0, 16, > &ofpacts); > + ofctrl_add_flow(flow_table, OFTABLE_CT_ORIG_TP_DST_LOAD, 100, 0, &match, > + &ofpacts, hc_uuid); > + > + /* Add the flow: > + * match = (ct.trk && ct.dnat && ip4), action = (reg4 = ct_nw_dst) > + * table = 81 > + */ > + ofpbuf_clear(&ofpacts); > + match_set_dl_type(&match, htons(ETH_TYPE_IP)); > + put_move(MFF_CT_NW_DST, 0, MFF_LOG_CT_ORIG_NW_DST_ADDR, 0, 32, > &ofpacts); > + ofctrl_add_flow(flow_table, OFTABLE_CT_ORIG_NW_DST_LOAD, 100, 0, &match, > + &ofpacts, hc_uuid); > + > + /* Add the flow: > + * match = (ct.trk && ct.dnat && ip6), action = (xxreg0 = ct_ip6_dst) > + * table = 82 > + */ > + ofpbuf_clear(&ofpacts); > + match_set_dl_type(&match, htons(ETH_TYPE_IPV6)); > + put_move(MFF_CT_IPV6_DST, 0, MFF_LOG_CT_ORIG_IP6_DST_ADDR, 0, > + 128, &ofpacts); > + ofctrl_add_flow(flow_table, OFTABLE_CT_ORIG_IP6_DST_LOAD, 100, 0, &match, > + &ofpacts, hc_uuid); > + > ofpbuf_uninit(&ofpacts); > } > diff --git a/include/ovn/actions.h b/include/ovn/actions.h > index 63a12a8821..91a6139b00 100644 > --- a/include/ovn/actions.h > +++ b/include/ovn/actions.h > @@ -131,6 +131,9 @@ struct collector_set_ids; > OVNACT(CHK_LB_AFF, ovnact_result) \ > OVNACT(SAMPLE, ovnact_sample) \ > OVNACT(MAC_CACHE_USE, ovnact_null) \ > + OVNACT(CT_ORIG_NW_DST, ovnact_result) \ > + OVNACT(CT_ORIG_IP6_DST, ovnact_result) \ > + OVNACT(CT_ORIG_TP_DST, ovnact_result) \ > > /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */ > enum OVS_PACKED_ENUM ovnact_type { > @@ -416,10 +419,11 @@ struct ovnact_set_queue { > uint16_t queue_id; > }; > > -/* OVNACT_DNS_LOOKUP, OVNACT_CHK_LB_HAIRPIN, OVNACT_CHK_LB_HAIRPIN_REPLY. */ > +/* OVNACT_DNS_LOOKUP, OVNACT_CHK_LB_HAIRPIN, OVNACT_CHK_LB_HAIRPIN_REPLY, > + * OVNACT_CT_ORIG_NW_DST, CT_ORIG_IP6_DST, CT_ORIG_TP_DST */ > struct ovnact_result { > struct ovnact ovnact; > - struct expr_field dst; /* 1-bit destination field. */ > + struct expr_field dst; /* destination field. */ > }; > > /* OVNACT_LOG. */ > @@ -935,6 +939,12 @@ struct ovnact_encode_params { > this determines which CT zone to use */ > uint32_t mac_cache_use_table; /* OpenFlow table for 'mac_cache_use' > * to resubmit. */ > + uint32_t ct_nw_dst_load_table; /* OpenFlow table for 'ct_nw_dst' > + * to resubmit. */ > + uint32_t ct_ip6_dst_load_table; /* OpenFlow table for 'ct_ip6_dst' > + * to resubmit. */ > + uint32_t ct_tp_dst_load_table; /* OpenFlow table for 'ct_tp_dst' > + * to resubmit. */ > }; > > void ovnacts_encode(const struct ovnact[], size_t ovnacts_len, > diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h > index d6c4a9b6b3..d563e044cb 100644 > --- a/include/ovn/logical-fields.h > +++ b/include/ovn/logical-fields.h > @@ -60,6 +60,10 @@ enum ovn_controller_event { > #define MFF_LOG_LB_AFF_MATCH_LR_IP6_ADDR MFF_XXREG1 > #define MFF_LOG_LB_AFF_MATCH_PORT MFF_REG8 > > +#define MFF_LOG_CT_ORIG_NW_DST_ADDR MFF_REG4 > +#define MFF_LOG_CT_ORIG_IP6_DST_ADDR MFF_XXREG0 > +#define MFF_LOG_CT_ORIG_TP_DST_PORT MFF_REG8 > + > void ovn_init_symtab(struct shash *symtab); > > /* MFF_LOG_FLAGS_REG bit assignments */ > diff --git a/lib/actions.c b/lib/actions.c > index c5bde996b7..d5fc30b27a 100644 > --- a/lib/actions.c > +++ b/lib/actions.c > @@ -3280,9 +3280,10 @@ ovnact_set_queue_free(struct ovnact_set_queue *a > OVS_UNUSED) > } > > static void > -parse_ovnact_result(struct action_context *ctx, const char *name, > - const char *prereq, const struct expr_field *dst, > - struct ovnact_result *res) > +parse_ovnact_result__(struct action_context *ctx, const char *name, > + const char *prereq, const struct expr_field *dst, > + struct ovnact_result *res, > + int n_bits) > { > lexer_get(ctx->lexer); /* Skip action name. */ > lexer_get(ctx->lexer); /* Skip '('. */ > @@ -3290,8 +3291,8 @@ parse_ovnact_result(struct action_context *ctx, const > char *name, > lexer_error(ctx->lexer, "%s doesn't take any parameters", name); > return; > } > - /* Validate that the destination is a 1-bit, modifiable field. */ > - char *error = expr_type_check(dst, 1, true, ctx->scope); > + /* Validate that the destination is n_bits, modifiable field. */ > + char *error = expr_type_check(dst, n_bits, true, ctx->scope); > if (error) { > lexer_error(ctx->lexer, "%s", error); > free(error); > @@ -3304,6 +3305,14 @@ parse_ovnact_result(struct action_context *ctx, const > char *name, > } > } > > +static void > +parse_ovnact_result(struct action_context *ctx, const char *name, > + const char *prereq, const struct expr_field *dst, > + struct ovnact_result *res) > +{ > + parse_ovnact_result__(ctx, name, prereq, dst, res, 1); > +} > + > static void > parse_dns_lookup(struct action_context *ctx, const struct expr_field *dst, > struct ovnact_result *dl) > @@ -4299,22 +4308,40 @@ format_CHK_LB_HAIRPIN_REPLY(const struct > ovnact_result *res, struct ds *s) > ds_put_cstr(s, " = chk_lb_hairpin_reply();"); > } > > +static void > +encode_result_action___(const struct ovnact_result *res, > + uint8_t resubmit_table, > + enum mf_field_id dst, > + int ofs, int n_bits, > + struct ofpbuf *ofpacts) > +{ > + ovs_assert(n_bits <= 128); > + > + struct mf_subfield res_dst = expr_resolve_field(&res->dst); > + ovs_assert(res_dst.field); > + > + put_load(0, dst, ofs, n_bits < 64 ? n_bits : 64, ofpacts); > + if (n_bits > 64) { > + put_load(0, dst, ofs + 64, n_bits - 64, ofpacts); > + } > + > + emit_resubmit(ofpacts, resubmit_table); > + > + struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts); > + orm->dst = res_dst; > + orm->src.field = mf_from_id(dst); > + orm->src.ofs = ofs; > + orm->src.n_bits = n_bits; > +} > + > static void > encode_result_action__(const struct ovnact_result *res, > uint8_t resubmit_table, > int log_flags_result_bit, > struct ofpbuf *ofpacts) > { > - struct mf_subfield dst = expr_resolve_field(&res->dst); > - ovs_assert(dst.field); > - put_load(0, MFF_LOG_FLAGS, log_flags_result_bit, 1, ofpacts); > - emit_resubmit(ofpacts, resubmit_table); > - > - struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts); > - orm->dst = dst; > - orm->src.field = mf_from_id(MFF_LOG_FLAGS); > - orm->src.ofs = log_flags_result_bit; > - orm->src.n_bits = 1; > + encode_result_action___(res, resubmit_table, MFF_LOG_FLAGS, > + log_flags_result_bit, 1, ofpacts); > } > > static void > @@ -5435,6 +5462,75 @@ encode_MAC_CACHE_USE(const struct ovnact_null *null > OVS_UNUSED, > emit_resubmit(ofpacts, ep->mac_cache_use_table); > } > > +static void > +encode_CT_ORIG_NW_DST(const struct ovnact_result *res, > + const struct ovnact_encode_params *ep, > + struct ofpbuf *ofpacts) > +{ > + encode_result_action___(res, ep->ct_nw_dst_load_table, > + MFF_LOG_CT_ORIG_NW_DST_ADDR, 0, 32, ofpacts); > +} > + > +static void > +parse_CT_ORIG_NW_DST(struct action_context *ctx, const struct expr_field > *dst, > + struct ovnact_result *res) > +{ > + parse_ovnact_result__(ctx, "ct_nw_dst", NULL, dst, res, 32); > +} > + > +static void > +format_CT_ORIG_NW_DST(const struct ovnact_result *res, struct ds *s) > +{ > + expr_field_format(&res->dst, s); > + ds_put_cstr(s, " = ct_nw_dst();"); > +} > + > +static void > +encode_CT_ORIG_IP6_DST(const struct ovnact_result *res, > + const struct ovnact_encode_params *ep, > + struct ofpbuf *ofpacts) > +{ > + encode_result_action___(res, ep->ct_ip6_dst_load_table, > + MFF_LOG_CT_ORIG_IP6_DST_ADDR, 0, 128, ofpacts); > +} > + > +static void > +parse_CT_ORIG_IP6_DST(struct action_context *ctx, const struct expr_field > *dst, > + struct ovnact_result *res) > +{ > + parse_ovnact_result__(ctx, "ct_ip6_dst", NULL, dst, res, 128); > +} > + > +static void > +format_CT_ORIG_IP6_DST(const struct ovnact_result *res, struct ds *s) > +{ > + expr_field_format(&res->dst, s); > + ds_put_cstr(s, " = ct_ip6_dst();"); > +} > + > +static void > +encode_CT_ORIG_TP_DST(const struct ovnact_result *res, > + const struct ovnact_encode_params *ep OVS_UNUSED, > + struct ofpbuf *ofpacts) > +{ > + encode_result_action___(res, ep->ct_tp_dst_load_table, > + MFF_LOG_CT_ORIG_TP_DST_PORT, 0, 16, ofpacts); > +} > + > +static void > +parse_CT_ORIG_TP_DST(struct action_context *ctx, const struct expr_field > *dst, > + struct ovnact_result *res) > +{ > + parse_ovnact_result__(ctx, "ct_tp_dst", NULL, dst, res, 16); > +} > + > +static void > +format_CT_ORIG_TP_DST(const struct ovnact_result *res, struct ds *s) > +{ > + expr_field_format(&res->dst, s); > + ds_put_cstr(s, " = ct_tp_dst();"); > +} > + > /* Parses an assignment or exchange or put_dhcp_opts action. */ > static void > parse_set_action(struct action_context *ctx) > @@ -5529,6 +5625,18 @@ parse_set_action(struct action_context *ctx) > } else if (lexer_match_id(ctx->lexer, "dhcp_relay_resp_chk")) { > parse_dhcp_relay_chk( > ctx, &lhs, ovnact_put_DHCPV4_RELAY_RESP_CHK(ctx->ovnacts)); > + } else if (!strcmp(ctx->lexer->token.s, "ct_nw_dst") && > + lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { > + parse_CT_ORIG_NW_DST(ctx, &lhs, > + ovnact_put_CT_ORIG_NW_DST(ctx->ovnacts)); > + } else if (!strcmp(ctx->lexer->token.s, "ct_ip6_dst") && > + lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { > + parse_CT_ORIG_IP6_DST(ctx, &lhs, > + ovnact_put_CT_ORIG_IP6_DST(ctx->ovnacts)); > + } else if (!strcmp(ctx->lexer->token.s, "ct_tp_dst") && > + lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { > + parse_CT_ORIG_TP_DST(ctx, &lhs, > + ovnact_put_CT_ORIG_TP_DST(ctx->ovnacts)); > } else { > parse_assignment_action(ctx, false, &lhs); > } > diff --git a/northd/northd.c b/northd/northd.c > index 3037ce0b55..985283a98e 100644 > --- a/northd/northd.c > +++ b/northd/northd.c > @@ -124,6 +124,7 @@ static bool vxlan_mode; > #define REGBIT_ACL_STATELESS "reg0[16]" > #define REGBIT_ACL_HINT_ALLOW_REL "reg0[17]" > #define REGBIT_FROM_ROUTER_PORT "reg0[18]" > +#define REGBIT_IP_FRAG "reg0[19]" > > #define REG_ORIG_DIP_IPV4 "reg1" > #define REG_ORIG_DIP_IPV6 "xxreg1" > @@ -6398,6 +6399,13 @@ build_pre_stateful(struct ovn_datapath *od, > > /* Note: priority-120 flows are added in build_lb_rules_pre_stateful(). > */ > > + /* If the packet is fragmented, set the REGBIT_IP_FRAG reg bit to 1 > + * as ip.is_frag will not be preserved after conntrack recirculation. */ > + ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 115, > + REGBIT_CONNTRACK_NAT" == 1 && ip.is_frag", > + REGBIT_IP_FRAG" = 1; ct_lb_mark;", > + lflow_ref); > + > ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 110, > REGBIT_CONNTRACK_NAT" == 1", "ct_lb_mark;", > lflow_ref); > @@ -8240,13 +8248,32 @@ build_lb_rules(struct lflow_table *lflows, struct > ovn_lb_datapaths *lb_dps, > struct ovn_lb_vip *lb_vip = &lb->vips[i]; > struct ovn_northd_lb_vip *lb_vip_nb = &lb->vips_nb[i]; > const char *ip_match = NULL; > + > + ds_clear(action); > + > + /* Store the original destination IP to be used when generating > + * hairpin flows. > + * If the packet is fragmented, then the flow which saves the > + * original destination IP (and port) in the "ls_in_pre_stateful" > + * stage will not be hit. > + */ > if (lb_vip->address_family == AF_INET) { > ip_match = "ip4"; > + ds_put_format(action, REG_ORIG_DIP_IPV4 " = %s; ", > + lb_vip->vip_str); > } else { > ip_match = "ip6"; > + ds_put_format(action, REG_ORIG_DIP_IPV6 " = %s; ", > + lb_vip->vip_str); > } > > - ds_clear(action); > + if (lb_vip->port_str) { > + /* Store the original destination port to be used when generating > + * hairpin flows. > + */ > + ds_put_format(action, REG_ORIG_TP_DPORT " = %s; ", > + lb_vip->port_str); > + } > ds_clear(match); > > /* New connections in Ingress table. */ > @@ -8378,8 +8405,32 @@ build_lb_hairpin(const struct ls_stateful_record > *ls_stateful_rec, > lflow_ref); > > if (ls_stateful_rec->has_lb_vip) { > - /* Check if the packet needs to be hairpinned. > - * Set REGBIT_HAIRPIN in the original direction and > + /* Check if the packet needs to be hairpinned. */ > + > + /* In order to check if the fragmented packets needs to be > + * hairpinned we need to save the ct tuple original IPv4/v6 > + * destination and L4 destination port in the registers after > + * the conntrack recirculation. > + * > + * Note: We are assuming that sending the packets to conntrack > + * will reassemble the packet and L4 fields will be available. > + * It is a risky assumption as ovs-vswitchd doesn't guarantee it > + * and userspace datapath doesn't reassemble the fragmented packets > + * after conntrack. It is the kernel datapath conntrack behavior. > + * We need to find a better way to handle the fragmented packets. > + * */ > + ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, 110, > + "ct.trk && !ct.rpl && "REGBIT_IP_FRAG" == 1 && ip4", Why do we do this only for packets in the original direction. Don't we need to be able to detect hairpin on replies too? I removed the !ct.rpl condition and things seem to work fine. > + REG_ORIG_DIP_IPV4 " = ct_nw_dst(); " > + REG_ORIG_TP_DPORT " = ct_tp_dst(); next;", > + lflow_ref); > + ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, 110, > + "ct.trk && !ct.rpl && "REGBIT_IP_FRAG" == 1 && ip6", > + REG_ORIG_DIP_IPV6 " = ct_ip6_dst(); " > + REG_ORIG_TP_DPORT " = ct_tp_dst(); next;", > + lflow_ref); > + > + /* Set REGBIT_HAIRPIN in the original direction and > * REGBIT_HAIRPIN_REPLY in the reply direction. > */ > ovn_lflow_add_with_hint( > diff --git a/ovn-sb.xml b/ovn-sb.xml > index 479d3e2851..ea4adc1c34 100644 > --- a/ovn-sb.xml > +++ b/ovn-sb.xml > @@ -2766,6 +2766,33 @@ tcp.flags = RST; > </p> > </dd> > > + <dt><code><var>R</var> = ct_nw_dst();</code></dt> > + <dd> > + <p> > + This action checks if the packet is tracked and stores the > + conntrack original destination IPv4 address in the register > + <var>R</var> of 32-bit size. > + </p> > + </dd> > + > + <dt><code><var>R</var> = ct_ip6_dst();</code></dt> > + <dd> > + <p> > + This action checks if the packet is tracked and stores the > + conntrack original destination IPv6 address in the register > + <var>R</var> of 128-bit size. > + </p> > + </dd> > + > + <dt><code><var>R</var> = ct_tp_dst();</code></dt> > + <dd> > + <p> > + This action checks if the packet is tracked and stores the > + conntrack original L4 destination port in the register > + <var>R</var> of 16-bit size. > + </p> > + </dd> > + > <dt><code>sample(probability=<var>packets</var>, ...)</code></dt> > <dd> > <p> > diff --git a/tests/ovn-macros.at b/tests/ovn-macros.at > index 77d1515f6f..a88556aa99 100644 > --- a/tests/ovn-macros.at > +++ b/tests/ovn-macros.at > @@ -1217,5 +1217,8 @@ m4_define([OFTABLE_ECMP_NH], [77]) > m4_define([OFTABLE_CHK_LB_AFFINITY], [78]) > m4_define([OFTABLE_MAC_CACHE_USE], [79]) > m4_define([OFTABLE_CT_ZONE_LOOKUP], [80]) > +m4_define([OFTABLE_CT_ORIG_NW_DST_LOAD], [81]) > +m4_define([OFTABLE_CT_ORIG_IP6_DST_LOAD], [82]) > +m4_define([OFTABLE_CT_ORIG_TP_DST_LOAD], [83]) > > m4_define([OFTABLE_SAVE_INPORT_HEX], [m4_eval(OFTABLE_SAVE_INPORT, 16)]) > diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at > index d1f7f105c0..e1386323a7 100644 > --- a/tests/ovn-northd.at > +++ b/tests/ovn-northd.at > @@ -1415,7 +1415,7 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb1 > AT_CAPTURE_FILE([sbflows]) > OVS_WAIT_FOR_OUTPUT( > [ovn-sbctl dump-flows sw0 | tee sbflows | grep 'priority=120.*backends' | > ovn_strip_lflows], 0, [dnl > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), > action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > ]) > > # disabled LSPs should not be a backend of Load Balancer > @@ -1424,7 +1424,7 @@ check ovn-nbctl lsp-set-enabled sw0-p1 disabled > AT_CAPTURE_FILE([sbflows]) > OVS_WAIT_FOR_OUTPUT( > [ovn-sbctl dump-flows sw0 | tee sbflows | grep 'priority=120.*backends' | > ovn_strip_lflows], 0, [dnl > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=20.0.0.3:80);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=20.0.0.3:80);) > ]) > wait_row_count Service_Monitor 1 > > @@ -1433,7 +1433,7 @@ check ovn-nbctl lsp-set-enabled sw0-p1 enabled > AT_CAPTURE_FILE([sbflows]) > OVS_WAIT_FOR_OUTPUT( > [ovn-sbctl dump-flows sw0 | tee sbflows | grep 'priority=120.*backends' | > ovn_strip_lflows], 0, [dnl > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), > action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > ]) > wait_row_count Service_Monitor 2 > > @@ -1444,7 +1444,7 @@ wait_row_count Service_Monitor 0 > AT_CAPTURE_FILE([sbflows2]) > OVS_WAIT_FOR_OUTPUT( > [ovn-sbctl dump-flows sw0 | tee sbflows2 | grep 'priority=120.*backends' | > ovn_strip_lflows], [0], > -[ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst > == 10.0.0.10 && tcp.dst == 80), > action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > +[ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst > == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > ]) > > AS_BOX([Create the Load_Balancer_Health_Check again.]) > @@ -1456,7 +1456,7 @@ check ovn-nbctl --wait=sb sync > > ovn-sbctl dump-flows sw0 | grep backends | grep priority=120 > lflows.txt > AT_CHECK([cat lflows.txt | ovn_strip_lflows], [0], [dnl > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), > action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > ]) > > AS_BOX([Get the uuid of both the service_monitor]) > @@ -1466,7 +1466,7 @@ sm_sw1_p1=$(fetch_column Service_Monitor _uuid > logical_port=sw1-p1) > AT_CAPTURE_FILE([sbflows3]) > OVS_WAIT_FOR_OUTPUT( > [ovn-sbctl dump-flows sw0 | tee sbflows 3 | grep 'priority=120.*backends' > | ovn_strip_lflows], [0], > -[ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst > == 10.0.0.10 && tcp.dst == 80), > action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > +[ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst > == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > ]) > > AS_BOX([Set the service monitor for sw1-p1 to offline]) > @@ -1477,7 +1477,7 @@ check ovn-nbctl --wait=sb sync > AT_CAPTURE_FILE([sbflows4]) > OVS_WAIT_FOR_OUTPUT( > [ovn-sbctl dump-flows sw0 | tee sbflows4 | grep 'priority=120.*backends' | > ovn_strip_lflows], [0], > -[ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst > == 10.0.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.3:80);) > +[ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst > == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.3:80);) > ]) > > AS_BOX([Set the service monitor for sw0-p1 to offline]) > @@ -1506,7 +1506,7 @@ check ovn-nbctl --wait=sb sync > AT_CAPTURE_FILE([sbflows7]) > OVS_WAIT_FOR_OUTPUT( > [ovn-sbctl dump-flows sw0 | tee sbflows7 | grep backends | grep > priority=120 | ovn_strip_lflows], 0, > -[ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst > == 10.0.0.10 && tcp.dst == 80), > action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > +[ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst > == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > ]) > > AS_BOX([Set the service monitor for sw1-p1 to error]) > @@ -1517,7 +1517,7 @@ check ovn-nbctl --wait=sb sync > ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \ > | grep priority=120 > lflows.txt > AT_CHECK([cat lflows.txt | grep ls_in_lb | ovn_strip_lflows], [0], [dnl > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.3:80);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.3:80);) > ]) > > AS_BOX([Add one more vip to lb1]) > @@ -1543,8 +1543,8 @@ AT_CAPTURE_FILE([sbflows9]) > OVS_WAIT_FOR_OUTPUT( > [ovn-sbctl dump-flows sw0 | tee sbflows9 | grep backends | grep > priority=120 | ovn_strip_lflows], > 0, > -[ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst > == 10.0.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.3:80);) > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.40 && tcp.dst == 1000), action=(ct_lb_mark(backends=10.0.0.3:1000);) > +[ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst > == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.3:80);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.40 && tcp.dst == 1000), action=(reg1 = 10.0.0.40; reg2[[0..15]] = > 1000; ct_lb_mark(backends=10.0.0.3:1000);) > ]) > > AS_BOX([Set the service monitor for sw1-p1 to online]) > @@ -1557,8 +1557,8 @@ AT_CAPTURE_FILE([sbflows10]) > OVS_WAIT_FOR_OUTPUT( > [ovn-sbctl dump-flows sw0 | tee sbflows10 | grep backends | grep > priority=120 | ovn_strip_lflows], > 0, > -[ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst > == 10.0.0.10 && tcp.dst == 80), > action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.40 && tcp.dst == 1000), > action=(ct_lb_mark(backends=10.0.0.3:1000,20.0.0.3:80);) > +[ table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst > == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.40 && tcp.dst == 1000), action=(reg1 = 10.0.0.40; reg2[[0..15]] = > 1000; ct_lb_mark(backends=10.0.0.3:1000,20.0.0.3:80);) > ]) > > AS_BOX([Associate lb1 to sw1]) > @@ -1567,8 +1567,8 @@ AT_CAPTURE_FILE([sbflows11]) > OVS_WAIT_FOR_OUTPUT( > [ovn-sbctl dump-flows sw1 | tee sbflows11 | grep backends | grep > priority=120 | ovn_strip_lflows], > 0, [dnl > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), > action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.40 && tcp.dst == 1000), > action=(ct_lb_mark(backends=10.0.0.3:1000,20.0.0.3:80);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.40 && tcp.dst == 1000), action=(reg1 = 10.0.0.40; reg2[[0..15]] = > 1000; ct_lb_mark(backends=10.0.0.3:1000,20.0.0.3:80);) > ]) > > AS_BOX([Now create lb2 same as lb1 but udp protocol.]) > @@ -4653,14 +4653,17 @@ check_stateful_flows() { > table=??(ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) > table=??(ls_in_pre_stateful ), priority=100 , match=(reg0[[0]] == 1), > action=(ct_next;) > table=??(ls_in_pre_stateful ), priority=110 , match=(reg0[[2]] == 1), > action=(ct_lb_mark;) > + table=??(ls_in_pre_stateful ), priority=115 , match=(reg0[[2]] == 1 && > ip.is_frag), action=(reg0[[19]] = 1; ct_lb_mark;) > table=??(ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && > ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; > reg2[[0..15]] = 80; ct_lb_mark;) > table=??(ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && > ip4.dst == 10.0.0.20 && tcp.dst == 80), action=(reg1 = 10.0.0.20; > reg2[[0..15]] = 80; ct_lb_mark;) > ]) > > AT_CHECK([grep "ls_in_lb " sw0flows | ovn_strip_lflows], [0], [dnl > table=??(ls_in_lb ), priority=0 , match=(1), action=(next;) > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.4:8080);) > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.20 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.40:8080);) > + table=??(ls_in_lb ), priority=110 , match=(ct.trk && !ct.rpl && > reg0[[19]] == 1 && ip4), action=(reg1 = ct_nw_dst(); reg2[[0..15]] = > ct_tp_dst(); next;) > + table=??(ls_in_lb ), priority=110 , match=(ct.trk && !ct.rpl && > reg0[[19]] == 1 && ip6), action=(xxreg1 = ct_ip6_dst(); reg2[[0..15]] = > ct_tp_dst(); next;) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.4:8080);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.20 && tcp.dst == 80), action=(reg1 = 10.0.0.20; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.40:8080);) > ]) > > AT_CHECK([grep "ls_in_stateful" sw0flows | ovn_strip_lflows], [0], [dnl > @@ -4724,6 +4727,7 @@ AT_CHECK([grep "ls_in_pre_stateful" sw0flows | > ovn_strip_lflows], [0], [dnl > table=??(ls_in_pre_stateful ), priority=0 , match=(1), action=(next;) > table=??(ls_in_pre_stateful ), priority=100 , match=(reg0[[0]] == 1), > action=(ct_next;) > table=??(ls_in_pre_stateful ), priority=110 , match=(reg0[[2]] == 1), > action=(ct_lb_mark;) > + table=??(ls_in_pre_stateful ), priority=115 , match=(reg0[[2]] == 1 && > ip.is_frag), action=(reg0[[19]] = 1; ct_lb_mark;) > ]) > > AT_CHECK([grep "ls_in_lb " sw0flows | ovn_strip_lflows], [0], [dnl > @@ -4764,6 +4768,8 @@ check ovn-nbctl --wait=sb ls-lb-add sw0 lb1 > AT_CHECK([ovn-sbctl dump-flows sw0 | grep "ls_in_lb " | ovn_strip_lflows ], > [0], [dnl > table=??(ls_in_lb ), priority=0 , match=(1), action=(next;) > table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == > 10.0.0.20), action=(drop;) > + table=??(ls_in_lb ), priority=110 , match=(ct.trk && !ct.rpl && > reg0[[19]] == 1 && ip4), action=(reg1 = ct_nw_dst(); reg2[[0..15]] = > ct_tp_dst(); next;) > + table=??(ls_in_lb ), priority=110 , match=(ct.trk && !ct.rpl && > reg0[[19]] == 1 && ip6), action=(xxreg1 = ct_ip6_dst(); reg2[[0..15]] = > ct_tp_dst(); next;) > ]) > > AT_CLEANUP > @@ -7763,7 +7769,9 @@ AT_CHECK([grep -e "ls_in_acl.*eval" -e "ls_in_acl_hint" > lsflows | ovn_strip_lflo > > AT_CHECK([grep -e "ls_in_lb " lsflows | ovn_strip_lflows], [0], [dnl > table=??(ls_in_lb ), priority=0 , match=(1), action=(next;) > - table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == > 10.0.0.2), action=(ct_lb_mark(backends=10.0.0.10);) > + table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == > 10.0.0.2), action=(reg1 = 10.0.0.2; ct_lb_mark(backends=10.0.0.10);) > + table=??(ls_in_lb ), priority=110 , match=(ct.trk && !ct.rpl && > reg0[[19]] == 1 && ip4), action=(reg1 = ct_nw_dst(); reg2[[0..15]] = > ct_tp_dst(); next;) > + table=??(ls_in_lb ), priority=110 , match=(ct.trk && !ct.rpl && > reg0[[19]] == 1 && ip6), action=(xxreg1 = ct_ip6_dst(); reg2[[0..15]] = > ct_tp_dst(); next;) > ]) > > AT_CHECK([grep -e "ls_in_stateful" lsflows | ovn_strip_lflows], [0], [dnl > @@ -7818,7 +7826,9 @@ AT_CHECK([grep -e "ls_in_acl.*eval" -e "ls_in_acl_hint" > lsflows | ovn_strip_lflo > > AT_CHECK([grep -e "ls_in_lb " lsflows | ovn_strip_lflows], [0], [dnl > table=??(ls_in_lb ), priority=0 , match=(1), action=(next;) > - table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == > 10.0.0.2), action=(ct_lb_mark(backends=10.0.0.10);) > + table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == > 10.0.0.2), action=(reg1 = 10.0.0.2; ct_lb_mark(backends=10.0.0.10);) > + table=??(ls_in_lb ), priority=110 , match=(ct.trk && !ct.rpl && > reg0[[19]] == 1 && ip4), action=(reg1 = ct_nw_dst(); reg2[[0..15]] = > ct_tp_dst(); next;) > + table=??(ls_in_lb ), priority=110 , match=(ct.trk && !ct.rpl && > reg0[[19]] == 1 && ip6), action=(xxreg1 = ct_ip6_dst(); reg2[[0..15]] = > ct_tp_dst(); next;) > ]) > > AT_CHECK([grep -e "ls_in_stateful" lsflows | ovn_strip_lflows], [0], [dnl > @@ -7873,7 +7883,9 @@ AT_CHECK([grep -e "ls_in_acl.*eval" -e "ls_in_acl_hint" > lsflows | ovn_strip_lflo > > AT_CHECK([grep -e "ls_in_lb " lsflows | ovn_strip_lflows], [0], [dnl > table=??(ls_in_lb ), priority=0 , match=(1), action=(next;) > - table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == > 10.0.0.2), action=(ct_lb_mark(backends=10.0.0.10);) > + table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == > 10.0.0.2), action=(reg1 = 10.0.0.2; ct_lb_mark(backends=10.0.0.10);) > + table=??(ls_in_lb ), priority=110 , match=(ct.trk && !ct.rpl && > reg0[[19]] == 1 && ip4), action=(reg1 = ct_nw_dst(); reg2[[0..15]] = > ct_tp_dst(); next;) > + table=??(ls_in_lb ), priority=110 , match=(ct.trk && !ct.rpl && > reg0[[19]] == 1 && ip6), action=(xxreg1 = ct_ip6_dst(); reg2[[0..15]] = > ct_tp_dst(); next;) > ]) > > AT_CHECK([grep -e "ls_in_stateful" lsflows | ovn_strip_lflows], [0], [dnl > @@ -9258,13 +9270,13 @@ AT_CAPTURE_FILE([S1flows]) > > AT_CHECK([grep "ls_in_lb " S0flows | ovn_strip_lflows], [0], [dnl > table=??(ls_in_lb ), priority=0 , match=(1), action=(next;) > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 172.16.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.2:80);) > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 172.16.0.11 && tcp.dst == 8080), action=(ct_lb_mark(backends=10.0.0.2:8080);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 172.16.0.10 && tcp.dst == 80), action=(reg1 = 172.16.0.10; reg2[[0..15]] = > 80; ct_lb_mark(backends=10.0.0.2:80);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 172.16.0.11 && tcp.dst == 8080), action=(reg1 = 172.16.0.11; reg2[[0..15]] = > 8080; ct_lb_mark(backends=10.0.0.2:8080);) > ]) > AT_CHECK([grep "ls_in_lb " S1flows | ovn_strip_lflows], [0], [dnl > table=??(ls_in_lb ), priority=0 , match=(1), action=(next;) > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 172.16.0.10 && tcp.dst == 80), action=(ct_lb_mark(backends=10.0.0.2:80);) > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 172.16.0.11 && tcp.dst == 8080), action=(ct_lb_mark(backends=10.0.0.2:8080);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 172.16.0.10 && tcp.dst == 80), action=(reg1 = 172.16.0.10; reg2[[0..15]] = > 80; ct_lb_mark(backends=10.0.0.2:80);) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 172.16.0.11 && tcp.dst == 8080), action=(reg1 = 172.16.0.11; reg2[[0..15]] = > 8080; ct_lb_mark(backends=10.0.0.2:8080);) > ]) > > ovn-sbctl get datapath S0 _uuid > dp_uuids > @@ -9394,7 +9406,9 @@ AT_CHECK([grep "ls_in_lb_aff_check" S0flows | > ovn_strip_lflows], [0], [dnl > ]) > AT_CHECK([grep "ls_in_lb " S0flows | ovn_strip_lflows], [0], [dnl > table=??(ls_in_lb ), priority=0 , match=(1), action=(next;) > - table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 172.16.0.10 && tcp.dst == 80), > action=(ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);) > + table=??(ls_in_lb ), priority=110 , match=(ct.trk && !ct.rpl && > reg0[[19]] == 1 && ip4), action=(reg1 = ct_nw_dst(); reg2[[0..15]] = > ct_tp_dst(); next;) > + table=??(ls_in_lb ), priority=110 , match=(ct.trk && !ct.rpl && > reg0[[19]] == 1 && ip6), action=(xxreg1 = ct_ip6_dst(); reg2[[0..15]] = > ct_tp_dst(); next;) > + table=??(ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 172.16.0.10 && tcp.dst == 80), action=(reg1 = 172.16.0.10; reg2[[0..15]] = > 80; ct_lb_mark(backends=10.0.0.2:80,20.0.0.2:80);) > table=??(ls_in_lb ), priority=150 , match=(reg9[[6]] == 1 && > ct.new && ip4.dst == 172.16.0.10 && reg4 == 10.0.0.2 && reg8[[0..15]] == 80), > action=(reg1 = 172.16.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.2:80);) > table=??(ls_in_lb ), priority=150 , match=(reg9[[6]] == 1 && > ct.new && ip4.dst == 172.16.0.10 && reg4 == 20.0.0.2 && reg8[[0..15]] == 80), > action=(reg1 = 172.16.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=20.0.0.2:80);) > ]) > @@ -13976,7 +13990,7 @@ AT_CHECK([grep "ls_in_pre_stateful" s1flows | > ovn_strip_lflows | grep "30.0.0.1" > table=??(ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && > ip4.dst == 30.0.0.1), action=(reg1 = 30.0.0.1; ct_lb_mark;) > ]) > AT_CHECK([grep "ls_in_lb" s1flows | ovn_strip_lflows | grep "30.0.0.1"], > [0], [dnl > - table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == > 30.0.0.1), > action=(ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);) > + table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == > 30.0.0.1), action=(reg1 = 30.0.0.1; > ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);) > ]) > > # Associate load balancer to lr1 with DGP > @@ -14090,7 +14104,7 @@ AT_CHECK([grep "ls_in_pre_stateful" s1flows | > ovn_strip_lflows | grep "2001:db8: > table=??(ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && > ip6.dst == 2001:db8:cccc::1), action=(xxreg1 = 2001:db8:cccc::1; ct_lb_mark;) > ]) > AT_CHECK([grep "ls_in_lb" s1flows | ovn_strip_lflows | grep > "2001:db8:cccc::1"], [0], [dnl > - table=??(ls_in_lb ), priority=110 , match=(ct.new && ip6.dst == > 2001:db8:cccc::1), > action=(ct_lb_mark(backends=2001:db8:aaaa:3::103,2001:db8:aaaa:3::102,2001:db8:aaaa:3::101);) > + table=??(ls_in_lb ), priority=110 , match=(ct.new && ip6.dst == > 2001:db8:cccc::1), action=(xxreg1 = 2001:db8:cccc::1; > ct_lb_mark(backends=2001:db8:aaaa:3::103,2001:db8:aaaa:3::102,2001:db8:aaaa:3::101);) > ]) > > # Associate load balancer to lr1 with DGP > @@ -14201,7 +14215,7 @@ AT_CHECK([grep "ls_in_pre_stateful" s1flows | > ovn_strip_lflows | grep "30.0.0.1" > table=??(ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && > ip4.dst == 30.0.0.1), action=(reg1 = 30.0.0.1; ct_lb_mark;) > ]) > AT_CHECK([grep "ls_in_lb" s1flows | ovn_strip_lflows | grep "30.0.0.1"], > [0], [dnl > - table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == > 30.0.0.1), > action=(ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);) > + table=??(ls_in_lb ), priority=110 , match=(ct.new && ip4.dst == > 30.0.0.1), action=(reg1 = 30.0.0.1; > ct_lb_mark(backends=172.16.0.103,172.16.0.102,172.16.0.101);) > ]) > > # Associate load balancer to lr1 with DGP > diff --git a/tests/ovn.at b/tests/ovn.at > index 10cd7a79b9..af3133f868 100644 > --- a/tests/ovn.at > +++ b/tests/ovn.at > @@ -2418,6 +2418,54 @@ sample(probability=10, obs_point=ct_label); > mac_cache_use; > encodes as resubmit(,OFTABLE_MAC_CACHE_USE) > > +# ct_nw_dst() > +reg1 = ct_nw_dst(); > + encodes as > set_field:0->reg4,resubmit(,OFTABLE_CT_ORIG_NW_DST_LOAD),move:NXM_NX_REG4[[]]->NXM_NX_XXREG0[[64..95]] > + > +xreg1[[3..34]] = ct_nw_dst(); > + encodes as > set_field:0->reg4,resubmit(,OFTABLE_CT_ORIG_NW_DST_LOAD),move:NXM_NX_REG4[[]]->NXM_NX_XXREG0[[3..34]] > + > +reg1[[3..34]] = ct_nw_dst(); > + Cannot select bits 3 to 34 of 32-bit field reg1. > + > +reg1[[3..35]] = ct_nw_dst(); > + Cannot select bits 3 to 35 of 32-bit field reg1. > + > +reg1[[1]] = ct_nw_dst(); > + Cannot use 1-bit field reg1[[1..1]] where 32-bit field is required. > + > +ct_nw_dst; > + Syntax error at `ct_nw_dst' expecting action. > + > +ct_nw_dst(); > + Syntax error at `ct_nw_dst' expecting action. > + > +# ct_ip6_dst() > +xxreg1 = ct_ip6_dst(); > + encodes as > set_field:0/0xffffffffffffffff->xxreg0,set_field:0/0xffffffffffffffff0000000000000000->xxreg0,resubmit(,OFTABLE_CT_ORIG_IP6_DST_LOAD),move:NXM_NX_XXREG0[[]]->NXM_NX_XXREG1[[]] > + > +reg1 = ct_ip6_dst(); > + Cannot use 32-bit field reg1[[0..31]] where 128-bit field is required. > + > +ct_ip6_dst; > + Syntax error at `ct_ip6_dst' expecting action. > + > +ct_ip6_dst(); > + Syntax error at `ct_ip6_dst' expecting action. > + > +# ct_tp_dst() > +reg1[[0..15]] = ct_tp_dst(); > + encodes as > set_field:0/0xffff->reg8,resubmit(,OFTABLE_CT_ORIG_TP_DST_LOAD),move:NXM_NX_REG8[[0..15]]->NXM_NX_XXREG0[[64..79]] > + > +reg1 = ct_tp_dst(); > + Cannot use 32-bit field reg1[[0..31]] where 16-bit field is required. > + > +ct_tp_dst; > + Syntax error at `ct_tp_dst' expecting action. > + > +ct_tp_dst(); > + Syntax error at `ct_tp_dst' expecting action. > + > # Miscellaneous negative tests. > ; > Syntax error at `;'. > @@ -25632,7 +25680,7 @@ OVS_WAIT_FOR_OUTPUT( > ovn-sbctl dump-flows sw0 | grep ct_lb_mark | grep priority=120 | sed > 's/table=..//'], 0, > [dnl > (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip4.dst == > 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark;) > - (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), > action=(ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80; > hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");) > + (ls_in_lb ), priority=120 , match=(ct.new && ip4.dst == > 10.0.0.10 && tcp.dst == 80), action=(reg1 = 10.0.0.10; reg2[[0..15]] = 80; > ct_lb_mark(backends=10.0.0.3:80,20.0.0.3:80; > hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");) > ]) > > AT_CAPTURE_FILE([sbflows2]) > @@ -25831,7 +25879,7 @@ OVS_WAIT_FOR_OUTPUT( > ovn-sbctl dump-flows sw0 | grep ct_lb_mark | grep priority=120 | sed > 's/table=..//'], 0, > [dnl > (ls_in_pre_stateful ), priority=120 , match=(reg0[[2]] == 1 && ip6.dst == > 2001::a && tcp.dst == 80), action=(xxreg1 = 2001::a; reg2[[0..15]] = 80; > ct_lb_mark;) > - (ls_in_lb ), priority=120 , match=(ct.new && ip6.dst == 2001::a > && tcp.dst == 80), action=(ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; > hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");) > + (ls_in_lb ), priority=120 , match=(ct.new && ip6.dst == 2001::a > && tcp.dst == 80), action=(xxreg1 = 2001::a; reg2[[0..15]] = 80; > ct_lb_mark(backends=[[2001::3]]:80,[[2002::3]]:80; > hash_fields="ip_dst,ip_src,tcp_dst,tcp_src");) > ]) > > AT_CAPTURE_FILE([sbflows2]) > @@ -35663,7 +35711,9 @@ check_default_flows() { > for table in $(grep -oP "table=\K\d*, " oflows | tr -d ',' | sort -n | > uniq); do > # Tables 68 and 70 are part of the chk_lb_hairpin and ct_snat_to_vip > actions > # respectively and it's OK if they don't have a default action. > - if test ${table} -eq 68 -o ${table} -eq 70; then > + # Tables 81, 82 and 83 are part of ct_nw_dst(), ct_ip6_dst() and > ct_tp_dst() > + # actions respectively and its OK for them to not have default flows. > + if test ${table} -eq 68 -o ${table} -eq 70 -o ${table} -eq 81 -o > ${table} -eq 82 -o ${table} -eq 83; then > continue; > fi > AT_CHECK([grep -qe "table=$table.* priority=0\(,metadata=0x\w*\)\? > actions" oflows], [0], [ignore], [ignore], [echo "Table $table does not > contain a default action"]) > diff --git a/tests/system-ovn-kmod.at b/tests/system-ovn-kmod.at > index 75ecdadebe..b17ec53f43 100644 > --- a/tests/system-ovn-kmod.at > +++ b/tests/system-ovn-kmod.at > @@ -1027,3 +1027,152 @@ OVS_TRAFFIC_VSWITCHD_STOP([" > "]) > AT_CLEANUP > ]) > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([Load Balancer LS hairpin IPv4 UDP - larger than MTU]) > +AT_SKIP_IF([test $HAVE_NC = no]) > +AT_SKIP_IF([test $HAVE_TCPDUMP = no]) We don't use tcpdump in the test. > +AT_KEYWORDS([lb]) > + > +ovn_start > + > +OVS_TRAFFIC_VSWITCHD_START() > +ADD_BR([br-int]) > + > +# Set external-ids in br-int needed for ovn-controller > +ovs-vsctl \ > + -- set Open_vSwitch . external-ids:system-id=hv1 \ > + -- set Open_vSwitch . > external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ > + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ > + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ > + -- set bridge br-int fail-mode=secure > other-config:disable-in-band=true > + > +# Start ovn-controller > +start_daemon ovn-controller > + > +# Logical network: > +# One logical switch with IPv4 load balancers that hairpin the traffic. > +ovn-nbctl ls-add sw Please add "check" in all places where it's applicable. > +ovn-nbctl lsp-add sw lsp -- lsp-set-addresses lsp 00:00:00:00:00:01 > +ovn-nbctl lb-add lb-ipv4-tcp 88.88.88.88:8080 42.42.42.1:4041 tcp We don't use this load balancer. > +ovn-nbctl lb-add lb-ipv4-udp 88.88.88.88:4040 42.42.42.1:2021 udp > +ovn-nbctl ls-lb-add sw lb-ipv4-tcp > +ovn-nbctl ls-lb-add sw lb-ipv4-udp > + > +ovn-nbctl lr-add rtr > +ovn-nbctl lrp-add rtr rtr-sw 00:00:00:00:01:00 42.42.42.254/24 > +ovn-nbctl lsp-add sw sw-rtr \ > + -- lsp-set-type sw-rtr router \ > + -- lsp-set-addresses sw-rtr 00:00:00:00:01:00 \ > + -- lsp-set-options sw-rtr router-port=rtr-sw > + > +ADD_NAMESPACES(lsp) > +ADD_VETH(lsp, lsp, br-int, "42.42.42.1/24", "00:00:00:00:00:01", \ > + "42.42.42.254") > + > +ovn-nbctl --wait=hv -t 3 sync > + No timeout needed. > +yes 1 | head -n 10000 | tr '\n' ' ' | dd of=datafile bs=7373 count=1 > +cat datafile datafile datafile datafile > frag_test.expected > + Maybe: dnl Generate a datafile that's 8KB long. printf %8s > datafile > +# Start IPv4 UDP server on lsp. > +NETNS_DAEMONIZE([lsp], [nc -l -u 42.42.42.1 2021 -o udp_frag_test.rcvd], > [lsp0_udp.pid]) > + > +NS_CHECK_EXEC([lsp], [nc -u 88.88.88.88 4040 -p 20000 < datafile], [0], > [ignore], [ignore]) > +NS_CHECK_EXEC([lsp], [nc -u 88.88.88.88 4040 -p 20000 < datafile], [0], > [ignore], [ignore]) > +NS_CHECK_EXEC([lsp], [nc -u 88.88.88.88 4040 -p 20000 < datafile], [0], > [ignore], [ignore]) > +NS_CHECK_EXEC([lsp], [nc -u 88.88.88.88 4040 -p 20000 < datafile], [0], > [ignore], [ignore]) > + This doesn't reply to UDP packets so the session doesn't move to established. Maybe we can try something like: NETNS_DAEMONIZE([lsp], [nc -e /bin/cat -u -v -l -o udp_frag_test.rcvd 2021], [lsp-nc.pid]) NS_CHECK_EXEC([lsp], [(cat datafile; sleep 10) | nc -u 88.88.88.88 4040]) This will make the server echo the incoming data. It will also dump all session data (incoming and outgoing) so we need to change the frag_test.expected contents (duplicate them). I added the ugly sleep 10 to avoid the client closing the socket too early - the kernel would generate ICMP port unreachable for the replies in that case. Maybe that can be done in a nicer way though. > +AT_CHECK([cmp frag_test.expected udp_frag_test.rcvd], [0], [ignore], > [ignore]) This could be: check cmp frag_test.expected udp_frag_test.rcvd Most of the system test comments above also apply to the IPv6 test below. > + > +OVS_APP_EXIT_AND_WAIT([ovn-controller]) > + > +as ovn-sb > +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) > + > +as ovn-nb > +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) > + > +as northd > +OVS_APP_EXIT_AND_WAIT([ovn-northd]) > + > +as > +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d > +/connection dropped.*/d"]) > +AT_CLEANUP > +]) > + > +OVN_FOR_EACH_NORTHD([ > +AT_SETUP([Load Balancer LS hairpin IPv6 UDP - larger than MTU]) > +AT_SKIP_IF([test $HAVE_NC = no]) > +AT_KEYWORDS([lb]) > + > +ovn_start > + > +OVS_TRAFFIC_VSWITCHD_START() > +ADD_BR([br-int]) > + > +# Set external-ids in br-int needed for ovn-controller > +ovs-vsctl \ > + -- set Open_vSwitch . external-ids:system-id=hv1 \ > + -- set Open_vSwitch . > external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \ > + -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \ > + -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \ > + -- set bridge br-int fail-mode=secure > other-config:disable-in-band=true > + > +# Start ovn-controller > +start_daemon ovn-controller > + > +# Logical network: > +# One logical switch with IPv6 load balancers that hairpin the traffic. > +ovn-nbctl ls-add sw > +ovn-nbctl lsp-add sw lsp -- lsp-set-addresses lsp 00:00:00:00:00:01 > +ovn-nbctl lb-add lb-ipv6-tcp [[8800::0088]]:8080 [[4200::1]]:4041 tcp > +ovn-nbctl lb-add lb-ipv6-udp [[8800::0088]]:4040 [[4200::1]]:2021 udp > +ovn-nbctl ls-lb-add sw lb-ipv6-tcp > +ovn-nbctl ls-lb-add sw lb-ipv6-udp > + > +ovn-nbctl lr-add rtr > +ovn-nbctl lrp-add rtr rtr-sw 00:00:00:00:01:00 4200::00ff/64 > +ovn-nbctl lsp-add sw sw-rtr \ > + -- lsp-set-type sw-rtr router \ > + -- lsp-set-addresses sw-rtr 00:00:00:00:01:00 \ > + -- lsp-set-options sw-rtr router-port=rtr-sw > + > +ovn-nbctl --wait=hv sync > + > +ADD_NAMESPACES(lsp) > +ADD_VETH(lsp, lsp, br-int, "4200::1/64", "00:00:00:00:00:01", "4200::00ff", > "nodad") > +ovn-nbctl --wait=hv -t 3 sync > + > +yes 1 | head -n 10000 | tr '\n' ' ' | dd of=datafile bs=7373 count=1 > +cat datafile datafile datafile datafile > frag_test.expected > + > +# Start IPv6 UDP server on lsp. > +NETNS_DAEMONIZE([lsp], [nc -l -u 4200::1 2021 -o udp_frag_test.rcvd], > [lsp0_udp.pid]) > + > +yes 1 | head -n 10000 | tr '\n' ' ' | dd of=datafile bs=7373 count=1 > +NS_CHECK_EXEC([lsp], [nc -6 -u 8800::0088 4040 -p 20001 < datafile], [0], > [ignore], [ignore]) > +NS_CHECK_EXEC([lsp], [nc -6 -u 8800::0088 4040 -p 20001 < datafile], [0], > [ignore], [ignore]) > +NS_CHECK_EXEC([lsp], [nc -6 -u 8800::0088 4040 -p 20001 < datafile], [0], > [ignore], [ignore]) > +NS_CHECK_EXEC([lsp], [nc -6 -u 8800::0088 4040 -p 20001 < datafile], [0], > [ignore], [ignore]) > + > +cat datafile datafile datafile datafile > udp_frag_test.expected > +AT_CHECK([cmp udp_frag_test.expected udp_frag_test.rcvd], [0], [ignore], > [ignore]) > + > +OVS_APP_EXIT_AND_WAIT([ovn-controller]) > + > +as ovn-sb > +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) > + > +as ovn-nb > +OVS_APP_EXIT_AND_WAIT([ovsdb-server]) > + > +as northd > +OVS_APP_EXIT_AND_WAIT([ovn-northd]) > + > +as > +OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d > +/connection dropped.*/d"]) > +AT_CLEANUP > +]) > diff --git a/tests/test-ovn.c b/tests/test-ovn.c > index 2ea68f2124..7954bb98ac 100644 > --- a/tests/test-ovn.c > +++ b/tests/test-ovn.c > @@ -1376,6 +1376,9 @@ test_parse_actions(struct ovs_cmdl_context *ctx > OVS_UNUSED) > .in_port_sec_ptable = OFTABLE_CHK_IN_PORT_SEC, > .out_port_sec_ptable = OFTABLE_CHK_OUT_PORT_SEC, > .mac_cache_use_table = OFTABLE_MAC_CACHE_USE, > + .ct_nw_dst_load_table = OFTABLE_CT_ORIG_NW_DST_LOAD, > + .ct_ip6_dst_load_table = OFTABLE_CT_ORIG_IP6_DST_LOAD, > + .ct_tp_dst_load_table = OFTABLE_CT_ORIG_TP_DST_LOAD, > .lflow_uuid.parts = > { 0xaaaaaaaa, 0xbbbbbbbb, 0xcccccccc, 0xdddddddd}, > .dp_key = 0xabcdef, > diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c > index f9cc2463b7..806bdf3d9a 100644 > --- a/utilities/ovn-trace.c > +++ b/utilities/ovn-trace.c > @@ -3447,6 +3447,12 @@ trace_actions(const struct ovnact *ovnacts, size_t > ovnacts_len, > break; > case OVNACT_MAC_CACHE_USE: > break; > + case OVNACT_CT_ORIG_NW_DST: > + break; > + case OVNACT_CT_ORIG_IP6_DST: > + break; > + case OVNACT_CT_ORIG_TP_DST: > + break; > } > } > ofpbuf_uninit(&stack); Thanks, Dumitru _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
