On 8/12/25 4:56 PM, Ales Musil via dev 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) {
join_datapaths() sets this value after calling ovn_datapath_update_external_ids() for the existing datapath. Is it a problem? > + 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; So, it seems like this value can never become false again, right? We may need a test for this. Same problem for the 'lb_with_stateless_mode', I suppose. > + } > } > > 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" > + 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. *defaults > + </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; _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev