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

Reply via email to