On Thu, Aug 14, 2025 at 12:41 PM Xavier Simonart <xsimo...@redhat.com> wrote:
> Hi Ales, Dumitru > > Thanks for the patch. > Two small nits below > > Acked-by: Xavier Simonart <xsimo...@redhat.com> > > Thanks > Xavier > Hi Xavier, thank you for the review. > > > On Tue, Aug 12, 2025 at 4:58 PM Ales Musil via dev < > ovs-dev@openvswitch.org> wrote: > >> Add extra action called "get_remote_fdb()", which is almost the same >> as "get_fdb()" but goes into the new OFTABLE_GET_REMOTE_FDB table. >> Use the new action in the logical flows to determine if the traffic >> should go towards the remote FDB. The current implementation will >> give a priority to remove FDB in case there is duplicate MAC between >> the SB FDB table and the remote FDB table. >> >> In order to make the logical action that assigns to two registers >> work, add default flows for the FDB and remote FDB side tables. >> The packet would be dropped otherwise if it would fall through >> the table without finding any FDB. This doesn't change >> the behavior for non-EVPN LS, because the packet is either dropped >> or will go towards the "unknown" multicast group as previously. >> >> There is also an option that will allow CMS to set FDB preference >> (local/remote), called "dynamic-routing-fdb-prefer-local" in LS >> other config. It defaults to "false", which means remote has the >> priority. >> >> Reported-at: https://issues.redhat.com/browse/FDP-1387 >> Signed-off-by: Ales Musil <amu...@redhat.com> >> --- >> v3: Rebase on top of latest main. >> Add the option to set FDB preference. >> >> v2: Rebase on top of latest main. >> --- >> NEWS | 3 ++ >> TODO.rst | 3 -- >> controller/lflow.c | 1 + >> controller/physical.c | 14 +++++++++ >> include/ovn/actions.h | 3 ++ >> lib/actions.c | 53 ++++++++++++++++++++++++++------ >> lib/logical-fields.c | 5 +++ >> lib/ovn-util.c | 4 +-- >> northd/northd.c | 71 +++++++++++++++++++++++++++++++++++-------- >> northd/northd.h | 3 ++ >> ovn-nb.xml | 21 +++++++++++++ >> tests/ovn-northd.at | 59 +++++++++++++++++++++++++++++++++++ >> tests/ovn.at | 19 ++++++++++-- >> tests/system-ovn.at | 19 ++++++++++++ >> tests/test-ovn.c | 1 + >> utilities/ovn-trace.c | 6 ++++ >> 16 files changed, 257 insertions(+), 28 deletions(-) >> >> diff --git a/NEWS b/NEWS >> index 9a7e2c8a8..eac073cac 100644 >> --- a/NEWS >> +++ b/NEWS >> @@ -54,6 +54,9 @@ Post v25.03.0 >> for the EVPN traffic egressing through the tunnel. >> * Add the "other_config:dynamic-routing-vni" to Logical Switches. >> If set >> to valid integer the LS is considered to be connected to EVPN L2 >> domain. >> + * Add the "other_config:dynamic-routing-fdb-prefer-local" to Logical >> + Switches. If set to "true" the LS will prefer SB FDB table for >> + FDB lookup. Default is false. >> >> OVN v25.03.0 - 07 Mar 2025 >> -------------------------- >> diff --git a/TODO.rst b/TODO.rst >> index 0d3c29506..9a9df78f2 100644 >> --- a/TODO.rst >> +++ b/TODO.rst >> @@ -157,6 +157,3 @@ OVN To-do List >> * Allow ovn-evpn-local-ip to accept list of >> $VNI1:$LOCAL_IP1,$VNI2:$LOCAL_IP2 combinations which will be properly >> reflected in physical flows for given LS with VNI. >> - >> - * Allow CMS to set FDB priority for EVPN, currently the remote FDB has >> - higher priority. >> diff --git a/controller/lflow.c b/controller/lflow.c >> index 04fb3ceed..b75ae5c0d 100644 >> --- a/controller/lflow.c >> +++ b/controller/lflow.c >> @@ -885,6 +885,7 @@ add_matches_to_flow_table(const struct >> sbrec_logical_flow *lflow, >> .lb_hairpin_reply_ptable = OFTABLE_CHK_LB_HAIRPIN_REPLY, >> .ct_snat_vip_ptable = OFTABLE_CT_SNAT_HAIRPIN, >> .fdb_ptable = OFTABLE_GET_FDB, >> + .remote_fdb_ptable = OFTABLE_GET_REMOTE_FDB, >> .fdb_lookup_ptable = OFTABLE_LOOKUP_FDB, >> .in_port_sec_ptable = OFTABLE_CHK_IN_PORT_SEC, >> .out_port_sec_ptable = OFTABLE_CHK_OUT_PORT_SEC, >> diff --git a/controller/physical.c b/controller/physical.c >> index a2b8c4071..60077ff71 100644 >> --- a/controller/physical.c >> +++ b/controller/physical.c >> @@ -3385,5 +3385,19 @@ physical_run(struct physical_ctx *p_ctx, >> physical_eval_remote_chassis_flows(p_ctx, &ofpacts, flow_table); >> physical_eval_evpn_flows(p_ctx, &ofpacts, flow_table); >> >> + /* Default flow for OFTABLE_GET_FDB table. */ >> + match_init_catchall(&match); >> + ofpbuf_clear(&ofpacts); >> + put_load(0, MFF_LOG_OUTPORT, 0, 32, &ofpacts); >> + ofctrl_add_flow(flow_table, OFTABLE_GET_FDB, 0, 0, >> + &match, &ofpacts, hc_uuid); >> + >> + /* Default flow for OFTABLE_GET_REMOTE_FDB table. */ >> + match_init_catchall(&match); >> + ofpbuf_clear(&ofpacts); >> + put_load(0, MFF_LOG_REMOTE_OUTPORT, 0, 32, &ofpacts); >> + ofctrl_add_flow(flow_table, OFTABLE_GET_REMOTE_FDB, 0, 0, >> + &match, &ofpacts, hc_uuid); >> + >> ofpbuf_uninit(&ofpacts); >> } >> diff --git a/include/ovn/actions.h b/include/ovn/actions.h >> index 30e5e4229..0eaef9112 100644 >> --- a/include/ovn/actions.h >> +++ b/include/ovn/actions.h >> @@ -121,6 +121,7 @@ struct collector_set_ids; >> OVNACT(SCTP_ABORT, ovnact_nest) \ >> OVNACT(PUT_FDB, ovnact_put_fdb) \ >> OVNACT(GET_FDB, ovnact_get_fdb) \ >> + OVNACT(GET_REMOTE_FDB, ovnact_get_fdb) \ >> OVNACT(LOOKUP_FDB, ovnact_lookup_fdb) \ >> OVNACT(CHECK_IN_PORT_SEC, ovnact_result) \ >> OVNACT(CHECK_OUT_PORT_SEC, ovnact_result) \ >> @@ -956,6 +957,8 @@ struct ovnact_encode_params { >> * 'ct_snat_to_vip' to resubmit. */ >> uint8_t fdb_ptable; /* OpenFlow table for >> * 'get_fdb' to resubmit. */ >> + uint8_t remote_fdb_ptable; /* OpenFlow table for >> + * 'get_remote_fdb' to resubmit. */ >> uint8_t fdb_lookup_ptable; /* OpenFlow table for >> * 'lookup_fdb' to resubmit. */ >> uint8_t in_port_sec_ptable; /* OpenFlow table for >> diff --git a/lib/actions.c b/lib/actions.c >> index 00e439285..98ab368fc 100644 >> --- a/lib/actions.c >> +++ b/lib/actions.c >> @@ -4385,19 +4385,22 @@ ovnact_put_fdb_free(struct ovnact_put_fdb >> *put_fdb OVS_UNUSED) >> } >> >> static void >> -format_GET_FDB(const struct ovnact_get_fdb *get_fdb, struct ds *s) >> +format_get_fdb(const struct ovnact_get_fdb *get_fdb, const char *type, >> + struct ds *s) >> { >> expr_field_format(&get_fdb->dst, s); >> - ds_put_cstr(s, " = get_fdb("); >> + ds_put_format(s, " = %s(", type); >> expr_field_format(&get_fdb->mac, s); >> ds_put_cstr(s, ");"); >> } >> >> static void >> -encode_GET_FDB(const struct ovnact_get_fdb *get_fdb, >> +encode_get_fdb(const struct ovnact_get_fdb *get_fdb, >> const struct ovnact_encode_params *ep, >> - struct ofpbuf *ofpacts) >> + bool remote, struct ofpbuf *ofpacts) >> { >> + const enum mf_field_id outport_id = >> + remote ? MFF_LOG_REMOTE_OUTPORT : MFF_LOG_OUTPORT; >> struct mf_subfield dst = expr_resolve_field(&get_fdb->dst); >> ovs_assert(dst.field); >> >> @@ -4405,25 +4408,53 @@ encode_GET_FDB(const struct ovnact_get_fdb >> *get_fdb, >> { expr_resolve_field(&get_fdb->mac), MFF_ETH_DST }, >> }; >> encode_setup_args(args, ARRAY_SIZE(args), ofpacts); >> - put_load(0, MFF_LOG_OUTPORT, 0, 32, ofpacts); >> - emit_resubmit(ofpacts, ep->fdb_ptable); >> + put_load(0, outport_id, 0, 32, ofpacts); >> + emit_resubmit(ofpacts, remote ? ep->remote_fdb_ptable : >> ep->fdb_ptable); >> encode_restore_args(args, ARRAY_SIZE(args), ofpacts); >> >> - if (dst.field->id != MFF_LOG_OUTPORT) { >> + if (dst.field->id != outport_id) { >> struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts); >> orm->dst = dst; >> - orm->src.field = mf_from_id(MFF_LOG_OUTPORT); >> + orm->src.field = mf_from_id(outport_id); >> orm->src.ofs = 0; >> orm->src.n_bits = 32; >> } >> } >> >> +static void >> +format_GET_FDB(const struct ovnact_get_fdb *get_fdb, struct ds *s) >> +{ >> + format_get_fdb(get_fdb, "get_fdb", s); >> +} >> + >> +static void >> +encode_GET_FDB(const struct ovnact_get_fdb *get_fdb, >> + const struct ovnact_encode_params *ep, >> + struct ofpbuf *ofpacts) >> +{ >> + encode_get_fdb(get_fdb, ep, false, ofpacts); >> +} >> + >> +static void >> +format_GET_REMOTE_FDB(const struct ovnact_get_fdb *get_fdb, struct ds *s) >> +{ >> + format_get_fdb(get_fdb, "get_remote_fdb", s); >> +} >> + >> +static void >> +encode_GET_REMOTE_FDB(const struct ovnact_get_fdb *get_fdb, >> + const struct ovnact_encode_params *ep, >> + struct ofpbuf *ofpacts) >> +{ >> + encode_get_fdb(get_fdb, ep, true, ofpacts); >> +} >> + >> static void >> parse_get_fdb(struct action_context *ctx, >> struct expr_field *dst, >> struct ovnact_get_fdb *get_fdb) >> { >> - lexer_get(ctx->lexer); /* Skip get_bfd. */ >> + lexer_get(ctx->lexer); /* Skip get_bfd/get_remote_fdb. */ >> lexer_get(ctx->lexer); /* Skip '('. */ >> >> /* Validate that the destination is a 32-bit, modifiable field if it >> @@ -5765,6 +5796,10 @@ parse_set_action(struct action_context *ctx) >> && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { >> parse_get_fdb( >> ctx, &lhs, ovnact_put_GET_FDB(ctx->ovnacts)); >> + } else if (!strcmp(ctx->lexer->token.s, "get_remote_fdb") >> + && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { >> + parse_get_fdb( >> + ctx, &lhs, ovnact_put_GET_REMOTE_FDB(ctx->ovnacts)); >> } else if (!strcmp(ctx->lexer->token.s, "lookup_fdb") >> && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) { >> parse_lookup_fdb( >> diff --git a/lib/logical-fields.c b/lib/logical-fields.c >> index f19eb579b..fcafeeac2 100644 >> --- a/lib/logical-fields.c >> +++ b/lib/logical-fields.c >> @@ -72,6 +72,11 @@ ovn_init_symtab(struct shash *symtab) >> expr_symtab_add_string(symtab, "inport", MFF_LOG_INPORT, NULL); >> expr_symtab_add_string(symtab, "outport", MFF_LOG_OUTPORT, NULL); >> >> + /* The port isn't reserved along the pipeline it's just defined as >> symbol >> + * to support matching on string and moving between string >> registers. */ >> + expr_symtab_add_string(symtab, "remote_outport", >> + MFF_LOG_REMOTE_OUTPORT, NULL); >> + >> /* Logical registers: >> * 128-bit xxregs >> * 64-bit xregs >> diff --git a/lib/ovn-util.c b/lib/ovn-util.c >> index 70a1c9404..fbba82758 100644 >> --- a/lib/ovn-util.c >> +++ b/lib/ovn-util.c >> @@ -915,8 +915,8 @@ ip_address_and_port_from_lb_key(const char *key, char >> **ip_address, >> * >> * NOTE: If OVN_NORTHD_PIPELINE_CSUM is updated make sure to double check >> * whether an update of OVN_INTERNAL_MINOR_VER is required. */ >> -#define OVN_NORTHD_PIPELINE_CSUM "1158333617 10744" >> -#define OVN_INTERNAL_MINOR_VER 9 >> +#define OVN_NORTHD_PIPELINE_CSUM "2405300854 10800" >> +#define OVN_INTERNAL_MINOR_VER 10 >> >> /* Returns the OVN version. The caller must free the returned value. */ >> char * >> diff --git a/northd/northd.c b/northd/northd.c >> index f02801f48..98d223177 100644 >> --- a/northd/northd.c >> +++ b/northd/northd.c >> @@ -254,12 +254,14 @@ static const char *reg_ct_state[] = { >> * | R0 | REGBIT_{CONNTRACK/DHCP/DNS} | | >> | >> * | | REGBIT_{HAIRPIN/HAIRPIN_REPLY} | | >> | >> * | | REGBIT_ACL_HINT_{ALLOW_NEW/ALLOW/DROP/BLOCK} | | >> | >> - * | | REGBIT_ACL_{LABEL/STATELESS} | X | >> | >> - * +----+----------------------------------------------+ X | >> | >> - * | R1 | REG_CT_TP_DST (0..15) | R | >> | >> - * | | REG_CT_PROTO (16..23) | E | >> | >> - * | | (>= IN_CT_EXTRACT && <= IN_LB_AFF_LEARN) | G | >> | >> - * +----+----------------------------------------------+ 0 | >> | >> + * | | REGBIT_ACL_{LABEL/STATELESS} | | >> | >> + * +----+----------------------------------------------+ | >> | >> + * | R1 | REG_CT_TP_DST (0..15) | | >> | >> + * | | REG_CT_PROTO (16..23) | | >> | >> + * | | (>= IN_CT_EXTRACT && <= IN_LB_AFF_LEARN) | X | >> | >> + * | | remote_outport | X | >> | >> + * | | (>= L2_LKUP && <= L2_UNKNOWN) | R | >> | >> + * +----+----------------------------------------------+ E | >> | >> * | R2 | REG_LB_PORT | G | >> | >> * | | (>= IN_PRE_STATEFUL && <= IN_LB_AFF_LEARN) | 0 | >> | >> * | | REG_ACL_ID | | >> | >> @@ -812,10 +814,10 @@ ovn_datapath_update_external_ids(struct >> ovn_datapath *od) >> "%u", age_threshold); >> } >> >> - int64_t vni = ovn_smap_get_llong(&od->nbs->other_config, >> - "dynamic-routing-vni", -1); >> - if (ovn_is_valid_vni(vni)) { >> - smap_add_format(&ids, "dynamic-routing-vni", "%"PRIi64, vni); >> + if (od->has_evpn_vni) { >> + const char *vni = smap_get(&od->nbs->other_config, >> + "dynamic-routing-vni"); >> + smap_add(&ids, "dynamic-routing-vni", vni); >> } >> } >> >> @@ -976,6 +978,12 @@ join_datapaths(const struct >> nbrec_logical_switch_table *nbrec_ls_table, >> false)) { >> od->lb_with_stateless_mode = true; >> } >> + >> + int64_t vni = ovn_smap_get_llong(&od->nbs->other_config, >> + "dynamic-routing-vni", -1); >> + if (ovn_is_valid_vni(vni)) { >> + od->has_evpn_vni = true; >> + } >> > nit: should we log (rl) when a invalid vni has been configured ? > We actually don't do that for any other config here. It's also a bit tricky as we would have to change the default. > } >> >> const struct nbrec_logical_router *nbr; >> @@ -5816,12 +5824,17 @@ build_lswitch_learn_fdb_od( >> struct lflow_ref *lflow_ref) >> { >> ovs_assert(od->nbs); >> + const char *lkp_action = od->has_evpn_vni >> + ? "outport = get_fdb(eth.dst); " >> + "remote_outport = get_remote_fdb(eth.dst); next;" >> + : "outport = get_fdb(eth.dst); next;"; >> + >> ovn_lflow_add(lflows, od, S_SWITCH_IN_LOOKUP_FDB, 0, "1", "next;", >> lflow_ref); >> ovn_lflow_add(lflows, od, S_SWITCH_IN_PUT_FDB, 0, "1", "next;", >> lflow_ref); >> ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 0, "1", >> - "outport = get_fdb(eth.dst); next;", lflow_ref); >> + lkp_action, lflow_ref); >> >> ovn_lflow_add(lflows, od, S_SWITCH_OUT_LOOKUP_FDB, 0, "1", "next;", >> lflow_ref); >> @@ -9351,6 +9364,36 @@ is_vlan_transparent(const struct ovn_datapath *od) >> return smap_get_bool(&od->nbs->other_config, "vlan-passthru", false); >> } >> >> +static void >> +build_lswitch_lflows_evpn_l2_unknown(struct ovn_datapath *od, >> + struct lflow_table *lflows, >> + struct lflow_ref *lflow_ref) >> +{ >> + if (od->has_unknown) { >> + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_UNKNOWN, 50, >> + "outport == \"none\" && remote_outport == >> \"none\"", >> + "outport = \""MC_UNKNOWN "\"; output;", lflow_ref); >> + } else { >> + ovn_lflow_add_drop_with_desc( >> + lflows, od, S_SWITCH_IN_L2_UNKNOWN, 50, "outport == \"none\" >> && " >> + "remote_outport == \"none\"", "No L2 destination", >> lflow_ref); >> + } >> + >> + if (smap_get_bool(&od->nbs->other_config, >> + "dynamic-routing-fdb-prefer-local", false)) { >> + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_UNKNOWN, 25, >> + "outport == \"none\"", >> + "outport = remote_outport; output;", lflow_ref); >> + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_UNKNOWN, 0, "1", >> + "output;", lflow_ref); >> + } else { >> + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_UNKNOWN, 25, >> + "remote_outport == \"none\"", "output;", >> lflow_ref); >> + ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_UNKNOWN, 0, "1", >> + "outport = remote_outport; output;", lflow_ref); >> + } >> +} >> + >> static void >> build_lswitch_lflows_l2_unknown(struct ovn_datapath *od, >> struct lflow_table *lflows, >> @@ -17604,7 +17647,11 @@ build_lswitch_and_lrouter_iterate_by_ls(struct >> ovn_datapath *od, >> ovn_lflow_add(lsi->lflows, od, S_SWITCH_IN_CT_EXTRACT, 0, "1", >> "next;", >> NULL); >> build_lswitch_lb_affinity_default_flows(od, lsi->lflows, NULL); >> - build_lswitch_lflows_l2_unknown(od, lsi->lflows, NULL); >> + if (od->has_evpn_vni) { >> + build_lswitch_lflows_evpn_l2_unknown(od, lsi->lflows, NULL); >> + } else { >> + build_lswitch_lflows_l2_unknown(od, lsi->lflows, NULL); >> + } >> build_mcast_flood_lswitch(od, lsi->lflows, &lsi->actions, NULL); >> } >> >> diff --git a/northd/northd.h b/northd/northd.h >> index 1108793d7..1235f3912 100644 >> --- a/northd/northd.h >> +++ b/northd/northd.h >> @@ -404,6 +404,9 @@ struct ovn_datapath { >> * This is applicable only to routers with "remote" ports. */ >> bool is_transit_router; >> >> + /* Indicates that the LS has valid vni associated with it. */ >> + bool has_evpn_vni; >> + >> /* OVN northd only needs to know about logical router gateway ports >> for >> * NAT/LB on a distributed router. The "distributed gateway ports" >> are >> * populated only when there is a gateway chassis or ha chassis group >> diff --git a/ovn-nb.xml b/ovn-nb.xml >> index c7c1fd6c7..84c4e2b88 100644 >> --- a/ovn-nb.xml >> +++ b/ovn-nb.xml >> @@ -886,6 +886,27 @@ >> removal/change in the future. >> </p> >> </column> >> + >> + <column name="other_config" >> key="dynamic-routing-evpn-fdb-prefer-local" >> > s/dynamic-routing-evpn-fdb-prefer-local/dynamic-routing-fdb-prefer-local/ > Nice catch, addressed in v4. > + type='{"type": "boolean"}'> >> + <p> >> + This option defines the preference of FDB lookup, if set to >> + true OVN will try to find the FDB entry in SB <code>FDB</code> >> + table first. Then it tries to resolve the FDB via >> + <code>ovn-controller</code> local EVPN FDB cache. The option >> + default to false. >> + </p> >> + >> + <p> >> + Only relevant if <ref column="other_config" >> key="dynamic-routing-vni" >> + table="Logical_switch"/> is set to valid >> VNI. >> + </p> >> + >> + <p> >> + NOTE: this feature is experimental and may be subject to >> + removal/change in the future. >> + </p> >> + </column> >> </group> >> >> <group title="IP Multicast Snooping Options"> >> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at >> index 05acd6f4d..303619a8b 100644 >> --- a/tests/ovn-northd.at >> +++ b/tests/ovn-northd.at >> @@ -17641,3 +17641,62 @@ check as northd ovn-appctl -t ovn-northd >> inc-engine/clear-stats >> >> AT_CLEANUP >> ]) >> + >> +OVN_FOR_EACH_NORTHD_NO_HV([ >> +AT_SETUP([LS dynamic-routing-fdb-prefer-local]) >> +ovn_start ovn-northd >> + >> +AS_BOX([Create logical switch.]) >> +check ovn-nbctl --wait=sb ls-add ls-evpn >> +ovn-sbctl dump-flows ls-evpn > lflows >> + >> +AT_CHECK([grep 'ls_in_l2_lkup' lflows | grep "get_fdb" | >> ovn_strip_lflows], [0], [dnl >> + table=??(ls_in_l2_lkup ), priority=0 , match=(1), >> action=(outport = get_fdb(eth.dst); next;) >> +]) >> + >> +AT_CHECK([grep 'ls_in_l2_unknown' lflows | ovn_strip_lflows], [0], [dnl >> + table=??(ls_in_l2_unknown ), priority=0 , match=(1), >> action=(output;) >> + table=??(ls_in_l2_unknown ), priority=50 , match=(outport == >> "none"), action=(drop;) >> +]) >> + >> +check ovn-nbctl --wait=hv set logical_switch ls-evpn >> other_config:dynamic-routing-vni=10 >> +ovn-sbctl dump-flows ls-evpn > lflows >> + >> +AT_CHECK([grep 'ls_in_l2_lkup' lflows | grep "get_fdb" | >> ovn_strip_lflows], [0], [dnl >> + table=??(ls_in_l2_lkup ), priority=0 , match=(1), >> action=(outport = get_fdb(eth.dst); remote_outport = >> get_remote_fdb(eth.dst); next;) >> +]) >> + >> +AT_CHECK([grep 'ls_in_l2_unknown' lflows | ovn_strip_lflows], [0], [dnl >> + table=??(ls_in_l2_unknown ), priority=0 , match=(1), >> action=(outport = remote_outport; output;) >> + table=??(ls_in_l2_unknown ), priority=25 , match=(remote_outport >> == "none"), action=(output;) >> + table=??(ls_in_l2_unknown ), priority=50 , match=(outport == >> "none" && remote_outport == "none"), action=(drop;) >> +]) >> + >> +check ovn-nbctl --wait=hv set logical_switch ls-evpn >> other_config:dynamic-routing-fdb-prefer-local=true >> +ovn-sbctl dump-flows ls-evpn > lflows >> + >> +AT_CHECK([grep 'ls_in_l2_lkup' lflows | grep "get_fdb" | >> ovn_strip_lflows], [0], [dnl >> + table=??(ls_in_l2_lkup ), priority=0 , match=(1), >> action=(outport = get_fdb(eth.dst); remote_outport = >> get_remote_fdb(eth.dst); next;) >> +]) >> + >> +AT_CHECK([grep 'ls_in_l2_unknown' lflows | ovn_strip_lflows], [0], [dnl >> + table=??(ls_in_l2_unknown ), priority=0 , match=(1), >> action=(output;) >> + table=??(ls_in_l2_unknown ), priority=25 , match=(outport == >> "none"), action=(outport = remote_outport; output;) >> + table=??(ls_in_l2_unknown ), priority=50 , match=(outport == >> "none" && remote_outport == "none"), action=(drop;) >> +]) >> + >> +check ovn-nbctl --wait=hv remove logical_switch ls-evpn other_config >> dynamic-routing-fdb-prefer-local >> +ovn-sbctl dump-flows ls-evpn > lflows >> + >> +AT_CHECK([grep 'ls_in_l2_lkup' lflows | grep "get_fdb" | >> ovn_strip_lflows], [0], [dnl >> + table=??(ls_in_l2_lkup ), priority=0 , match=(1), >> action=(outport = get_fdb(eth.dst); remote_outport = >> get_remote_fdb(eth.dst); next;) >> +]) >> + >> +AT_CHECK([grep 'ls_in_l2_unknown' lflows | ovn_strip_lflows], [0], [dnl >> + table=??(ls_in_l2_unknown ), priority=0 , match=(1), >> action=(outport = remote_outport; output;) >> + table=??(ls_in_l2_unknown ), priority=25 , match=(remote_outport >> == "none"), action=(output;) >> + table=??(ls_in_l2_unknown ), priority=50 , match=(outport == >> "none" && remote_outport == "none"), action=(drop;) >> +]) >> + >> +AT_CLEANUP >> +]) >> diff --git a/tests/ovn.at b/tests/ovn.at >> index 6fbfc7fa9..dd8ef1e06 100644 >> --- a/tests/ovn.at >> +++ b/tests/ovn.at >> @@ -2087,6 +2087,13 @@ reg15 = get_fdb(eth.dst); >> outport = get_fdb(ip4.dst); >> Cannot use 32-bit field ip4.dst[[0..31]] where 48-bit field is >> required. >> >> +# get_remote_fdb >> +reg1 = get_remote_fdb(eth.dst); >> + encodes as >> set_field:0->reg1,resubmit(,OFTABLE_GET_REMOTE_FDB),move:NXM_NX_REG1[[]]->NXM_NX_XXREG0[[64..95]] >> + >> +reg1 = get_remote_fdb(eth.src); >> + encodes as >> push:NXM_OF_ETH_DST[[]],push:NXM_OF_ETH_SRC[[]],pop:NXM_OF_ETH_DST[[]],set_field:0->reg1,resubmit(,OFTABLE_GET_REMOTE_FDB),pop:NXM_OF_ETH_DST[[]],move:NXM_NX_REG1[[]]->NXM_NX_XXREG0[[64..95]] >> + >> # lookup_fdb >> reg0[[0]] = lookup_fdb(inport, eth.src); >> encodes as >> set_field:0/0x100->reg10,resubmit(,OFTABLE_LOOKUP_FDB),move:NXM_NX_REG10[[8]]->NXM_NX_XXREG0[[96]] >> @@ -32987,10 +32994,12 @@ as hv2 ovs-ofctl dump-flows br-int >> table=OFTABLE_GET_FDB > hv2_offlows_table71.t >> AT_CAPTURE_FILE([hv1_offlows_table71.txt]) >> AT_CAPTURE_FILE([hv2_offlows_table71.txt]) >> AT_CHECK_UNQUOTED([cat hv1_offlows_table71.txt | grep -v NXST | cut -d ' >> ' -f8- | sort], [0], [dnl >> +priority=0 actions=load:0->NXM_NX_REG15[[]] >> priority=100,metadata=0x$dp_key,dl_dst=50:54:00:00:00:13 >> actions=load:0x$port_key->NXM_NX_REG15[[]] >> ]) >> >> AT_CHECK_UNQUOTED([cat hv2_offlows_table71.txt | grep -v NXST | cut -d ' >> ' -f8- | sort], [0], [dnl >> +priority=0 actions=load:0->NXM_NX_REG15[[]] >> priority=100,metadata=0x$dp_key,dl_dst=50:54:00:00:00:13 >> actions=load:0x$port_key->NXM_NX_REG15[[]] >> ]) >> >> @@ -33024,6 +33033,7 @@ AT_CAPTURE_FILE([hv3_offlows_table71.txt]) >> AT_CAPTURE_FILE([hv3_offlows_table72.txt]) >> >> AT_CHECK_UNQUOTED([cat hv3_offlows_table71.txt | grep -v NXST | cut -d ' >> ' -f8- | sort], [0], [dnl >> +priority=0 actions=load:0->NXM_NX_REG15[[]] >> priority=100,metadata=0x$dp_key,dl_dst=50:54:00:00:00:13 >> actions=load:0x$port_key->NXM_NX_REG15[[]] >> ]) >> >> @@ -33056,16 +33066,19 @@ AT_CAPTURE_FILE([hv1_offlows_table71.txt]) >> AT_CAPTURE_FILE([hv2_offlows_table71.txt]) >> AT_CAPTURE_FILE([hv3_offlows_table71.txt]) >> AT_CHECK_UNQUOTED([cat hv1_offlows_table71.txt | grep -v NXST | cut -d ' >> ' -f8- | sort], [0], [dnl >> +priority=0 actions=load:0->NXM_NX_REG15[[]] >> priority=100,metadata=0x$dp_key,dl_dst=50:54:00:00:00:13 >> actions=load:0x$port_key->NXM_NX_REG15[[]] >> priority=100,metadata=0x$dp_key,dl_dst=50:54:00:00:00:14 >> actions=load:0x$port_key->NXM_NX_REG15[[]] >> ]) >> >> AT_CHECK_UNQUOTED([cat hv2_offlows_table71.txt | grep -v NXST | cut -d ' >> ' -f8- | sort], [0], [dnl >> +priority=0 actions=load:0->NXM_NX_REG15[[]] >> priority=100,metadata=0x$dp_key,dl_dst=50:54:00:00:00:13 >> actions=load:0x$port_key->NXM_NX_REG15[[]] >> priority=100,metadata=0x$dp_key,dl_dst=50:54:00:00:00:14 >> actions=load:0x$port_key->NXM_NX_REG15[[]] >> ]) >> >> AT_CHECK_UNQUOTED([cat hv3_offlows_table71.txt | grep -v NXST | cut -d ' >> ' -f8- | sort], [0], [dnl >> +priority=0 actions=load:0->NXM_NX_REG15[[]] >> priority=100,metadata=0x$dp_key,dl_dst=50:54:00:00:00:13 >> actions=load:0x$port_key->NXM_NX_REG15[[]] >> priority=100,metadata=0x$dp_key,dl_dst=50:54:00:00:00:14 >> actions=load:0x$port_key->NXM_NX_REG15[[]] >> ]) >> @@ -33258,10 +33271,12 @@ as hv2 ovs-ofctl dump-flows br-int >> table=OFTABLE_GET_FDB > hv2_offlows_table71.t >> >> AT_CAPTURE_FILE([hv1_offlows_table71.txt]) >> AT_CAPTURE_FILE([hv2_offlows_table71.txt]) >> -AT_CHECK([cat hv1_offlows_table71.txt | grep -v NXST], [1], [dnl >> +AT_CHECK([cat hv1_offlows_table71.txt | grep -v NXST | cut -d ' ' -f8- | >> sort], [0], [dnl >> +priority=0 actions=load:0->NXM_NX_REG15[[]] >> ]) >> >> -AT_CHECK([cat hv2_offlows_table71.txt | grep -v NXST], [1], [dnl >> +AT_CHECK([cat hv2_offlows_table71.txt | grep -v NXST | cut -d ' ' -f8- | >> sort], [0], [dnl >> +priority=0 actions=load:0->NXM_NX_REG15[[]] >> ]) >> >> as hv1 ovs-ofctl dump-flows br-int table=OFTABLE_LOOKUP_FDB > >> hv1_offlows_table72.txt >> diff --git a/tests/system-ovn.at b/tests/system-ovn.at >> index 9c1e0e0dd..2366f513c 100644 >> --- a/tests/system-ovn.at >> +++ b/tests/system-ovn.at >> @@ -18417,18 +18417,37 @@ AT_CHECK([grep "resubmit" >> oftable_remote_vtep_output | grep -c "load:0xa900000a" >> AT_CHECK([grep "resubmit" oftable_remote_vtep_output | grep -c >> "load:0xa9000014"], [0], [3 >> ]) >> >> +# Simulate remote workload. >> +check bridge fdb add f0:00:0f:16:10:50 dev vxlan-$vni dst 169.0.0.10 >> static extern_learn >> +check bridge fdb add f0:00:0f:16:10:60 dev vxlan-$vni dst 169.0.0.20 >> static extern_learn >> + >> +OVS_WAIT_FOR_OUTPUT_UNQUOTED([ovn-appctl evpn/vtep-fdb-list | cut -d',' >> -f2- | sort], [0], [dnl >> + MAC: f0:00:0f:16:10:50, binding_key: 0x80000001, dp_key: $dp_key >> + MAC: f0:00:0f:16:10:60, binding_key: 0x80000002, dp_key: $dp_key >> +]) >> + >> +AT_CHECK_UNQUOTED([ovs-ofctl dump-flows br-int >> table=OFTABLE_GET_REMOTE_FDB | grep priority | \ >> + awk '{print $7, $8}' | sort], [0], [dnl >> +priority=0 actions=load:0->NXM_NX_REG1[[]] >> +priority=150,metadata=0x$dp_key,dl_dst=f0:00:0f:16:10:50 >> actions=load:0x80000001->NXM_NX_REG1[[]] >> +priority=150,metadata=0x$dp_key,dl_dst=f0:00:0f:16:10:60 >> actions=load:0x80000002->NXM_NX_REG1[[]] >> +]) >> + >> # Check that the recompute won't change the UUIDs and tunnel keys. >> ovn-appctl evpn/vtep-binding-list > bindings_before >> ovn-appctl evpn/vtep-multicast-group-list > mc_groups_before >> +ovn-appctl evpn/vtep-fdb-list > fdb_before >> >> check ovn-appctl inc-engine/recompute >> check ovn-nbctl --wait=hv sync >> >> ovn-appctl evpn/vtep-binding-list > bindings_after >> ovn-appctl evpn/vtep-multicast-group-list > mc_groups_after >> +ovn-appctl evpn/vtep-fdb-list > fdb_after >> >> check diff -q bindings_before bindings_after >> check diff -q mc_groups_before mc_groups_after >> +check diff -q fdb_before fdb_after >> >> OVN_CLEANUP_CONTROLLER([hv1]) >> >> diff --git a/tests/test-ovn.c b/tests/test-ovn.c >> index 1580f44c4..fae7e7bd5 100644 >> --- a/tests/test-ovn.c >> +++ b/tests/test-ovn.c >> @@ -1380,6 +1380,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx >> OVS_UNUSED) >> .lb_hairpin_reply_ptable = OFTABLE_CHK_LB_HAIRPIN_REPLY, >> .ct_snat_vip_ptable = OFTABLE_CT_SNAT_HAIRPIN, >> .fdb_ptable = OFTABLE_GET_FDB, >> + .remote_fdb_ptable = OFTABLE_GET_REMOTE_FDB, >> .fdb_lookup_ptable = OFTABLE_LOOKUP_FDB, >> .common_nat_ct_zone = MFF_LOG_DNAT_ZONE, >> .in_port_sec_ptable = OFTABLE_CHK_IN_PORT_SEC, >> diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c >> index a63a3be19..caeec9619 100644 >> --- a/utilities/ovn-trace.c >> +++ b/utilities/ovn-trace.c >> @@ -3536,6 +3536,12 @@ trace_actions(const struct ovnact *ovnacts, size_t >> ovnacts_len, >> execute_get_fdb(ovnact_get_GET_FDB(a), dp, uflow); >> break; >> >> + case OVNACT_GET_REMOTE_FDB: >> + ovntrace_node_append(super, OVNTRACE_NODE_OUTPUT, >> + "/* The remote FDB table is different" >> + " per each chassis. */"); >> + break; >> + >> case OVNACT_LOOKUP_FDB: >> execute_lookup_fdb(ovnact_get_LOOKUP_FDB(a), dp, uflow, >> super); >> break; >> -- >> 2.50.1 >> >> _______________________________________________ >> dev mailing list >> d...@openvswitch.org >> https://mail.openvswitch.org/mailman/listinfo/ovs-dev >> >> Thanks, Ales _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev