On Fri, Jul 25, 2025 at 8:04 AM Ales Musil <amu...@redhat.com> wrote:

> Create FDB structure and flows based on the FDB learned from the
> netlink neighbor table. This is required to make the traffic unicast.
> We can do the mapping between binding and remote VTEP FDB by
> populating the remote FDB side table.
>
> The flows in that table will be utilized in future commit.
>
> Signed-off-by: Ales Musil <amu...@redhat.com>
> ---
>

The reason why the CI is failing here is that the lflow.h change from
the next commit should have really been in this one. I will fix that
in v2 once there are more comments.

 controller/automake.mk              |   2 +
>  controller/evpn-fdb.c               | 151 ++++++++++++++++++++++++++++
>  controller/evpn-fdb.h               |  59 +++++++++++
>  controller/lflow.h                  |   1 +
>  controller/neighbor-exchange-stub.c |   5 +
>  controller/neighbor-exchange.c      |  77 +++++++++++++-
>  controller/neighbor-exchange.h      |  13 +++
>  controller/ovn-controller.c         | 122 +++++++++++++++++++++-
>  controller/physical.c               |  48 +++++++++
>  controller/physical.h               |   4 +
>  tests/ovn-macros.at                 |   1 +
>  11 files changed, 481 insertions(+), 2 deletions(-)
>  create mode 100644 controller/evpn-fdb.c
>  create mode 100644 controller/evpn-fdb.h
>
> diff --git a/controller/automake.mk b/controller/automake.mk
> index 8d94fb646..d53d932c1 100644
> --- a/controller/automake.mk
> +++ b/controller/automake.mk
> @@ -12,6 +12,8 @@ controller_ovn_controller_SOURCES = \
>         controller/encaps.h \
>         controller/evpn-binding.c \
>         controller/evpn-binding.h \
> +       controller/evpn-fdb.c \
> +       controller/evpn-fdb.h \
>         controller/ha-chassis.c \
>         controller/ha-chassis.h \
>         controller/if-status.c \
> diff --git a/controller/evpn-fdb.c b/controller/evpn-fdb.c
> new file mode 100644
> index 000000000..02318cde2
> --- /dev/null
> +++ b/controller/evpn-fdb.c
> @@ -0,0 +1,151 @@
> +/* Copyright (c) 2025, Red Hat, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +
> +#include "evpn-binding.h"
> +#include "neighbor-exchange.h"
> +#include "openvswitch/dynamic-string.h"
> +#include "openvswitch/vlog.h"
> +#include "packets.h"
> +#include "unixctl.h"
> +
> +#include "evpn-fdb.h"
> +
> +VLOG_DEFINE_THIS_MODULE(evpn_fdb);
> +
> +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> +
> +static struct evpn_fdb *evpn_fdb_add(struct hmap *evpn_fdbs, struct
> eth_addr);
> +static struct evpn_fdb *evpn_fdb_find(const struct hmap *evpn_fdbs,
> +                                      struct eth_addr);
> +
> +void
> +evpn_fdb_run(const struct evpn_fdb_ctx_in *f_ctx_in,
> +             struct evpn_fdb_ctx_out *f_ctx_out)
> +{
> +    struct hmapx stale_fdbs = HMAPX_INITIALIZER(&stale_fdbs);
> +
> +    struct evpn_fdb *fdb;
> +    HMAP_FOR_EACH (fdb, hmap_node, f_ctx_out->fdbs) {
> +        hmapx_add(&stale_fdbs, fdb);
> +    }
> +
> +    const struct evpn_static_fdb *static_fdb;
> +    HMAP_FOR_EACH (static_fdb, hmap_node, f_ctx_in->static_fdbs) {
> +        const struct evpn_binding *binding =
> +            evpn_binding_find(f_ctx_in->bindings, &static_fdb->ip,
> +                              static_fdb->vni);
> +        if (!binding) {
> +            VLOG_WARN_RL(&rl, "Couldn't find EVPN binding for
> "ETH_ADDR_FMT" "
> +                         "MAC address.", ETH_ADDR_ARGS(static_fdb->mac));
> +            continue;
> +        }
> +
> +        fdb = evpn_fdb_find(f_ctx_out->fdbs, static_fdb->mac);
> +        if (!fdb) {
> +            fdb = evpn_fdb_add(f_ctx_out->fdbs, static_fdb->mac);
> +        }
> +
> +        bool updated = false;
> +        if (fdb->binding_key != binding->tunnel_key) {
> +            fdb->binding_key = binding->tunnel_key;
> +            updated = true;
> +        }
> +
> +        if (fdb->dp_key != binding->dp_key) {
> +            fdb->dp_key = binding->dp_key;
> +            updated = true;
> +        }
> +
> +        if (updated) {
> +            hmapx_add(f_ctx_out->updated_fdbs, fdb);
> +        }
> +
> +        hmapx_find_and_delete(&stale_fdbs, fdb);
> +    }
> +
> +    struct hmapx_node *node;
> +    HMAPX_FOR_EACH (node, &stale_fdbs) {
> +        fdb = node->data;
> +
> +        uuidset_insert(f_ctx_out->removed_fdbs, &fdb->flow_uuid);
> +        hmap_remove(f_ctx_out->fdbs, &fdb->hmap_node);
> +        free(fdb);
> +    }
> +
> +    hmapx_destroy(&stale_fdbs);
> +}
> +
> +void
> +evpn_fdbs_destroy(struct hmap *fdbs)
> +{
> +    struct evpn_fdb *fdb;
> +    HMAP_FOR_EACH_POP (fdb, hmap_node, fdbs) {
> +        free(fdb);
> +    }
> +    hmap_destroy(fdbs);
> +}
> +
> +void
> +evpn_fdb_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
> +              const char *argv[] OVS_UNUSED, void *data_)
> +{
> +    struct hmap *fdbs = data_;
> +    struct ds ds = DS_EMPTY_INITIALIZER;
> +
> +    const struct evpn_fdb *fdb;
> +    HMAP_FOR_EACH (fdb, hmap_node, fdbs) {
> +        ds_put_format(&ds, "UUID: "UUID_FMT", MAC: "ETH_ADDR_FMT", "
> +                      "binding_key: %#"PRIx32", dp_key: %"PRIu32"\n",
> +                      UUID_ARGS(&fdb->flow_uuid), ETH_ADDR_ARGS(fdb->mac),
> +                      fdb->binding_key, fdb->dp_key);
> +    }
> +
> +    unixctl_command_reply(conn, ds_cstr_ro(&ds));
> +    ds_destroy(&ds);
> +}
> +
> +static struct evpn_fdb *
> +evpn_fdb_add(struct hmap *evpn_fdbs, struct eth_addr mac)
> +{
> +    struct evpn_fdb *fdb = xmalloc(sizeof *fdb);
> +    *fdb = (struct evpn_fdb) {
> +        .flow_uuid = uuid_random(),
> +        .mac = mac,
> +        .binding_key = 0,
> +        .dp_key = 0,
> +    };
> +
> +    uint32_t hash = hash_bytes(&mac, sizeof mac, 0);
> +    hmap_insert(evpn_fdbs, &fdb->hmap_node, hash);
> +
> +    return fdb;
> +}
> +
> +static struct evpn_fdb *
> +evpn_fdb_find(const struct hmap *evpn_fdbs, struct eth_addr mac)
> +{
> +    uint32_t hash = hash_bytes(&mac, sizeof mac, 0);
> +
> +    struct evpn_fdb *fdb;
> +    HMAP_FOR_EACH_WITH_HASH (fdb, hmap_node, hash, evpn_fdbs) {
> +        if (eth_addr_equals(fdb->mac, mac)) {
> +            return fdb;
> +        }
> +    }
> +
> +    return NULL;
> +}
> diff --git a/controller/evpn-fdb.h b/controller/evpn-fdb.h
> new file mode 100644
> index 000000000..a38718cd9
> --- /dev/null
> +++ b/controller/evpn-fdb.h
> @@ -0,0 +1,59 @@
> +/* Copyright (c) 2025, Red Hat, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef EVPN_FDB_H
> +#define EVPN_FDB_H 1
> +
> +#include <stdint.h>
> +
> +#include "hmapx.h"
> +#include "openvswitch/hmap.h"
> +#include "uuidset.h"
> +
> +struct unixctl_conn;
> +
> +struct evpn_fdb_ctx_in {
> +    /* Contains 'struct evpn_binding'. */
> +    const struct hmap *bindings;
> +    /* Contains 'struct evpn_static_fdb'. */
> +    const struct hmap *static_fdbs;
> +};
> +
> +struct evpn_fdb_ctx_out {
> +    /* Contains 'struct evpn_fdb'. */
> +    struct hmap *fdbs;
> +    /* Contains pointers to 'struct evpn_binding'. */
> +    struct hmapx *updated_fdbs;
> +    /* Contains 'flow_uuid' from removed 'struct evpn_binding'. */
> +    struct uuidset *removed_fdbs;
> +};
> +
> +struct evpn_fdb {
> +    struct hmap_node hmap_node;
> +    /* UUID used to identify physical flows related to this FDB. */
> +    struct uuid flow_uuid;
> +    /* IP address of the remote VTEP. */
> +    struct eth_addr mac;
> +    /* Local tunnel key to identify the binding. */
> +    uint32_t binding_key;
> +    uint32_t dp_key;
> +};
> +
> +void evpn_fdb_run(const struct evpn_fdb_ctx_in *, struct evpn_fdb_ctx_out
> *);
> +void evpn_fdbs_destroy(struct hmap *fdbs);
> +void evpn_fdb_list(struct unixctl_conn *conn, int argc,
> +                   const char *argv[], void *data_);
> +
> +#endif /* EVPN_FDB_H */
> diff --git a/controller/lflow.h b/controller/lflow.h
> index c2d277078..c8a87c886 100644
> --- a/controller/lflow.h
> +++ b/controller/lflow.h
> @@ -102,6 +102,7 @@ struct uuid;
>  #define OFTABLE_FLOOD_REMOTE_CHASSIS     84
>  #define OFTABLE_CT_STATE_SAVE            85
>  #define OFTABLE_CT_ORIG_PROTO_LOAD       86
> +#define OFTABLE_GET_REMOTE_FDB           87
>
>  /* Common defines shared between some controller components. */
>  #define CHASSIS_FLOOD_INDEX_START 0x8000
> diff --git a/controller/neighbor-exchange-stub.c
> b/controller/neighbor-exchange-stub.c
> index d314543be..272c14007 100644
> --- a/controller/neighbor-exchange-stub.c
> +++ b/controller/neighbor-exchange-stub.c
> @@ -40,3 +40,8 @@ evpn_remote_vtep_list(struct unixctl_conn *conn
> OVS_UNUSED,
>                        void *data_ OVS_UNUSED)
>  {
>  }
> +
> +void
> +evpn_static_fdbs_clear(struct hmap *static_fdbs OVS_UNUSED)
> +{
> +}
> diff --git a/controller/neighbor-exchange.c
> b/controller/neighbor-exchange.c
> index 0fec5e59d..43b1477c6 100644
> --- a/controller/neighbor-exchange.c
> +++ b/controller/neighbor-exchange.c
> @@ -31,6 +31,7 @@
>  VLOG_DEFINE_THIS_MODULE(neighbor_exchange);
>
>  static bool neighbor_is_valid_remote_vtep(struct ne_nl_received_neigh *);
> +static bool neighbor_is_valid_static_fdb(struct ne_nl_received_neigh *);
>  static uint32_t evpn_remote_vtep_hash(const struct in6_addr *ip,
>                                        uint16_t port, uint32_t vni);
>  static void evpn_remote_vtep_add(struct hmap *remote_vteps, struct
> in6_addr ip,
> @@ -38,7 +39,13 @@ static void evpn_remote_vtep_add(struct hmap
> *remote_vteps, struct in6_addr ip,
>  static struct evpn_remote_vtep *evpn_remote_vtep_find(
>      const struct hmap *remote_vteps, struct in6_addr *ip,
>      uint16_t port, uint32_t vni);
> -
> +static void evpn_static_fdb_add(struct hmap *static_fdbs, struct eth_addr
> mac,
> +                                struct in6_addr ip, uint32_t vni);
> +static struct evpn_static_fdb *evpn_static_fdb_find(
> +    const struct hmap *static_fdbs, struct eth_addr mac,
> +    struct in6_addr ip, uint32_t vni);
> +static uint32_t evpn_static_fdb_hash(const struct eth_addr *mac,
> +                                     const struct in6_addr *ip, uint32_t
> vni);
>
>  /* Last neighbor_exchange netlink operation. */
>  static int neighbor_exchange_nl_status;
> @@ -94,6 +101,13 @@ neighbor_exchange_run(const struct
> neighbor_exchange_ctx_in *n_ctx_in,
>                          evpn_remote_vtep_add(n_ctx_out->remote_vteps,
> ne->addr,
>                                               port, nim->vni);
>                      }
> +                } else if (neighbor_is_valid_static_fdb(ne)) {
> +                    if (!evpn_static_fdb_find(n_ctx_out->static_fdbs,
> +                                              ne->lladdr, ne->addr,
> +                                              nim->vni)) {
> +                        evpn_static_fdb_add(n_ctx_out->static_fdbs,
> ne->lladdr,
> +                                            ne->addr, nim->vni);
> +                    }
>                  }
>              }
>          }
> @@ -117,6 +131,13 @@ neighbor_is_valid_remote_vtep(struct
> ne_nl_received_neigh *ne)
>             ne->state & NUD_PERMANENT;
>  }
>
> +static bool
> +neighbor_is_valid_static_fdb(struct ne_nl_received_neigh *ne)
> +{
> +    return !eth_addr_is_zero(ne->lladdr) &&
> +           ipv6_addr_is_set(&ne->addr) && ne->flags & NTF_EXT_LEARNED;
> +}
> +
>  void
>  evpn_remote_vteps_clear(struct hmap *remote_vteps)
>  {
> @@ -145,6 +166,15 @@ evpn_remote_vtep_list(struct unixctl_conn *conn, int
> argc OVS_UNUSED,
>      ds_destroy(&ds);
>  }
>
> +void
> +evpn_static_fdbs_clear(struct hmap *static_fdbs)
> +{
> +    struct evpn_static_fdb *fdb;
> +    HMAP_FOR_EACH_POP (fdb, hmap_node, static_fdbs) {
> +        free(fdb);
> +    }
> +}
> +
>  static void
>  evpn_remote_vtep_add(struct hmap *remote_vteps, struct in6_addr ip,
>                       uint16_t port, uint32_t vni)
> @@ -188,3 +218,48 @@ evpn_remote_vtep_hash(const struct in6_addr *ip,
> uint16_t port,
>
>      return hash;
>  }
> +
> +static void
> +evpn_static_fdb_add(struct hmap *static_fdbs, struct eth_addr mac,
> +                    struct in6_addr ip, uint32_t vni)
> +{
> +    struct evpn_static_fdb *fdb = xmalloc(sizeof *fdb);
> +    *fdb = (struct evpn_static_fdb) {
> +        .mac = mac,
> +        .ip = ip,
> +        .vni = vni,
> +    };
> +
> +    hmap_insert(static_fdbs, &fdb->hmap_node,
> +                evpn_static_fdb_hash(&mac, &ip, vni));
> +}
> +
> +static struct evpn_static_fdb *
> +evpn_static_fdb_find(const struct hmap *static_fdbs, struct eth_addr mac,
> +                     struct in6_addr ip, uint32_t vni)
> +{
> +    uint32_t hash = evpn_static_fdb_hash(&mac, &ip, vni);
> +
> +    struct evpn_static_fdb *fdb;
> +    HMAP_FOR_EACH_WITH_HASH (fdb, hmap_node, hash, static_fdbs) {
> +        if (eth_addr_equals(fdb->mac, mac) &&
> +            ipv6_addr_equals(&fdb->ip, &ip) &&
> +            fdb->vni == vni) {
> +            return fdb;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
> +static uint32_t
> +evpn_static_fdb_hash(const struct eth_addr *mac, const struct in6_addr
> *ip,
> +                     uint32_t vni)
> +{
> +    uint32_t hash = 0;
> +    hash = hash_bytes(mac, sizeof *mac, hash);
> +    hash = hash_add_in6_addr(hash, ip);
> +    hash = hash_add(hash, vni);
> +
> +    return hash;
> +}
> diff --git a/controller/neighbor-exchange.h
> b/controller/neighbor-exchange.h
> index dba97fdb9..208107909 100644
> --- a/controller/neighbor-exchange.h
> +++ b/controller/neighbor-exchange.h
> @@ -34,6 +34,8 @@ struct neighbor_exchange_ctx_out {
>      struct hmap neighbor_table_watches;
>      /* Contains 'struct evpn_remote_vtep'. */
>      struct hmap *remote_vteps;
> +    /* Contains 'struct evpn_static_fdb'. */
> +    struct hmap *static_fdbs;
>  };
>
>  struct evpn_remote_vtep {
> @@ -46,11 +48,22 @@ struct evpn_remote_vtep {
>      uint32_t vni;
>  };
>
> +struct evpn_static_fdb {
> +    struct hmap_node hmap_node;
> +    /* MAC address of the remote workload. */
> +    struct eth_addr mac;
> +    /* Destination ip of the remote tunnel. */
> +    struct in6_addr ip;
> +    /* VNI of the VTEP. */
> +    uint32_t vni;
> +};
> +
>  void neighbor_exchange_run(const struct neighbor_exchange_ctx_in *,
>                             struct neighbor_exchange_ctx_out *);
>  int neighbor_exchange_status_run(void);
>  void evpn_remote_vteps_clear(struct hmap *remote_vteps);
>  void evpn_remote_vtep_list(struct unixctl_conn *conn, int argc,
>                             const char *argv[], void *data_);
> +void evpn_static_fdbs_clear(struct hmap *static_fdbs);
>
>  #endif  /* NEIGHBOR_EXCHANGE_H */
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index 2f2959edb..a3d4f28c6 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -99,6 +99,7 @@
>  #include "neighbor-exchange.h"
>  #include "neighbor-table-notify.h"
>  #include "evpn-binding.h"
> +#include "evpn-fdb.h"
>
>  VLOG_DEFINE_THIS_MODULE(main);
>
> @@ -4576,6 +4577,15 @@ struct ed_type_evpn_vtep_binding {
>      struct hmap tunnel_keys;
>  };
>
> +struct ed_type_evpn_fdb {
> +    /* Contains 'struct evpn_fdb'. */
> +    struct hmap fdbs;
> +    /* Contains pointers to 'struct evpn_fdb'. */
> +    struct hmapx updated_fdbs;
> +    /* Contains 'flow_uuid' from removed 'struct evpn_fdb'. */
> +    struct uuidset removed_fdbs;
> +};
> +
>  static void init_physical_ctx(struct engine_node *node,
>                                struct ed_type_runtime_data *rt_data,
>                                struct ed_type_non_vif_data *non_vif_data,
> @@ -4633,6 +4643,9 @@ static void init_physical_ctx(struct engine_node
> *node,
>      struct ed_type_evpn_vtep_binding *eb_data =
>          engine_get_input_data("evpn_vtep_binding", node);
>
> +    struct ed_type_evpn_fdb *efdb_data =
> +        engine_get_input_data("evpn_fdb", node);
> +
>      parse_encap_ips(ovs_table, &p_ctx->n_encap_ips, &p_ctx->encap_ips);
>      p_ctx->sbrec_port_binding_by_name = sbrec_port_binding_by_name;
>      p_ctx->sbrec_port_binding_by_datapath =
> sbrec_port_binding_by_datapath;
> @@ -4652,6 +4665,7 @@ static void init_physical_ctx(struct engine_node
> *node,
>      p_ctx->always_tunnel = n_opts->always_tunnel;
>      p_ctx->evpn_bindings = &eb_data->bindings;
>      p_ctx->evpn_multicast_groups = &eb_data->multicast_groups;
> +    p_ctx->evpn_fdbs = &efdb_data->fdbs;
>
>      struct controller_engine_ctx *ctrl_ctx =
> engine_get_context()->client_ctx;
>      p_ctx->if_mgr = ctrl_ctx->if_mgr;
> @@ -4964,6 +4978,18 @@ pflow_output_evpn_binding_handler(struct
> engine_node *node, void *data)
>      return EN_HANDLED_UPDATED;
>  }
>
> +static enum engine_input_handler_result
> +pflow_output_fdb_handler(struct engine_node *node, void *data)
> +{
> +    struct ed_type_pflow_output *pfo = data;
> +    struct ed_type_evpn_fdb *ef_data =
> +        engine_get_input_data("evpn_fdb", node);
> +
> +    physical_handle_evpn_fdb_changes(&pfo->flow_table,
> &ef_data->updated_fdbs,
> +                                     &ef_data->removed_fdbs);
> +    return EN_HANDLED_UPDATED;
> +}
> +
>  static void *
>  en_controller_output_init(struct engine_node *node OVS_UNUSED,
>                            struct engine_arg *arg OVS_UNUSED)
> @@ -5897,6 +5923,8 @@ en_neighbor_table_notify_run(struct engine_node
> *node OVS_UNUSED,
>  struct ed_type_neighbor_exchange {
>      /* Contains 'struct evpn_remote_vtep'. */
>      struct hmap remote_vteps;
> +    /* Contains 'struct evpn_static_fdb'. */
> +    struct hmap static_fdbs;
>  };
>
>  static void *
> @@ -5906,6 +5934,7 @@ en_neighbor_exchange_init(struct engine_node *node
> OVS_UNUSED,
>      struct ed_type_neighbor_exchange *data = xmalloc(sizeof *data);
>      *data = (struct ed_type_neighbor_exchange) {
>          .remote_vteps = HMAP_INITIALIZER(&data->remote_vteps),
> +        .static_fdbs = HMAP_INITIALIZER(&data->static_fdbs),
>      };
>
>      return data;
> @@ -5916,7 +5945,9 @@ en_neighbor_exchange_cleanup(void *data_)
>  {
>      struct ed_type_neighbor_exchange *data = data_;
>      evpn_remote_vteps_clear(&data->remote_vteps);
> +    evpn_static_fdbs_clear(&data->static_fdbs);
>      hmap_destroy(&data->remote_vteps);
> +    hmap_destroy(&data->static_fdbs);
>  }
>
>  static enum engine_node_state
> @@ -5927,6 +5958,7 @@ en_neighbor_exchange_run(struct engine_node *node,
> void *data_)
>          engine_get_input_data("neighbor", node);
>
>      evpn_remote_vteps_clear(&data->remote_vteps);
> +    evpn_static_fdbs_clear(&data->static_fdbs);
>
>      struct neighbor_exchange_ctx_in n_ctx_in = {
>          .monitored_interfaces = &neighbor_data->monitored_interfaces,
> @@ -5935,6 +5967,7 @@ en_neighbor_exchange_run(struct engine_node *node,
> void *data_)
>          .neighbor_table_watches =
>              HMAP_INITIALIZER(&n_ctx_out.neighbor_table_watches),
>          .remote_vteps = &data->remote_vteps,
> +        .static_fdbs = &data->static_fdbs,
>      };
>
>      neighbor_exchange_run(&n_ctx_in, &n_ctx_out);
> @@ -6094,7 +6127,7 @@ evpn_vtep_binding_datapath_binding_handler(struct
> engine_node *node,
>  {
>      const struct sbrec_datapath_binding_table *dp_table =
>          EN_OVSDB_GET(engine_get_input("SB_datapath_binding", node));
> -    struct ed_type_runtime_data *rt_data =
> +    const struct ed_type_runtime_data *rt_data =
>          engine_get_input_data("runtime_data", node);
>
>      const struct sbrec_datapath_binding *dp;
> @@ -6126,6 +6159,81 @@ evpn_vtep_binding_datapath_binding_handler(struct
> engine_node *node,
>      return EN_HANDLED_UNCHANGED;
>  }
>
> +static void *
> +en_evpn_fdb_init(struct engine_node *node OVS_UNUSED,
> +                 struct engine_arg *arg OVS_UNUSED)
> +{
> +    struct ed_type_evpn_fdb *data = xmalloc(sizeof *data);
> +    *data = (struct ed_type_evpn_fdb) {
> +        .fdbs = HMAP_INITIALIZER(&data->fdbs),
> +        .updated_fdbs = HMAPX_INITIALIZER(&data->updated_fdbs),
> +        .removed_fdbs = UUIDSET_INITIALIZER(&data->removed_fdbs),
> +    };
> +
> +    return data;
> +}
> +
> +static void
> +en_evpn_fdb_clear_tracked_data(void *data_)
> +{
> +    struct ed_type_evpn_fdb *data = data_;
> +    hmapx_clear(&data->updated_fdbs);
> +    uuidset_clear(&data->removed_fdbs);
> +}
> +
> +static void
> +en_evpn_fdb_cleanup(void *data_)
> +{
> +    struct ed_type_evpn_fdb *data = data_;
> +    evpn_fdbs_destroy(&data->fdbs);
> +    hmapx_destroy(&data->updated_fdbs);
> +    uuidset_destroy(&data->removed_fdbs);
> +}
> +
> +static enum engine_node_state
> +en_evpn_fdb_run(struct engine_node *node, void *data_)
> +{
> +    struct ed_type_evpn_fdb *data = data_;
> +    const struct ed_type_neighbor_exchange *ne_data =
> +        engine_get_input_data("neighbor_exchange", node);
> +    const struct ed_type_evpn_vtep_binding *eb_data =
> +        engine_get_input_data("evpn_vtep_binding", node);
> +
> +    struct evpn_fdb_ctx_in f_ctx_in = {
> +        .static_fdbs = &ne_data->static_fdbs,
> +        .bindings = &eb_data->bindings,
> +    };
> +
> +    struct evpn_fdb_ctx_out f_ctx_out = {
> +        .fdbs = &data->fdbs,
> +        .updated_fdbs = &data->updated_fdbs,
> +        .removed_fdbs = &data->removed_fdbs,
> +    };
> +
> +    evpn_fdb_run(&f_ctx_in, &f_ctx_out);
> +
> +    if (hmapx_count(&data->updated_fdbs) ||
> +        uuidset_count(&data->removed_fdbs)) {
> +        return EN_UPDATED;
> +    }
> +
> +    return EN_UNCHANGED;
> +}
> +
> +static enum engine_input_handler_result
> +evpn_fdb_vtep_binding_handler(struct engine_node *node, void *data
> OVS_UNUSED)
> +{
> +    const struct ed_type_evpn_vtep_binding *eb_data =
> +        engine_get_input_data("evpn_vtep_binding", node);
> +
> +    if (hmapx_is_empty(&eb_data->updated_bindings) &&
> +        uuidset_is_empty(&eb_data->removed_bindings)) {
> +        return EN_HANDLED_UNCHANGED;
> +    }
> +
> +    return EN_UNHANDLED;
> +}
> +
>  /* Returns false if the northd internal version stored in SB_Global
>   * and ovn-controller internal version don't match.
>   */
> @@ -6441,6 +6549,7 @@ main(int argc, char *argv[])
>      ENGINE_NODE(neighbor_exchange);
>      ENGINE_NODE(neighbor_exchange_status);
>      ENGINE_NODE(evpn_vtep_binding, CLEAR_TRACKED_DATA);
> +    ENGINE_NODE(evpn_fdb, CLEAR_TRACKED_DATA);
>
>  #define SB_NODE(NAME) ENGINE_NODE_SB(NAME);
>      SB_NODES
> @@ -6688,8 +6797,14 @@ main(int argc, char *argv[])
>      engine_add_input(&en_evpn_vtep_binding, &en_sb_datapath_binding,
>                       evpn_vtep_binding_datapath_binding_handler);
>
> +    engine_add_input(&en_evpn_fdb, &en_neighbor_exchange, NULL);
> +    engine_add_input(&en_evpn_fdb, &en_evpn_vtep_binding,
> +                     evpn_fdb_vtep_binding_handler);
> +
>      engine_add_input(&en_pflow_output, &en_evpn_vtep_binding,
>                       pflow_output_evpn_binding_handler);
> +    engine_add_input(&en_pflow_output, &en_evpn_fdb,
> +                     pflow_output_fdb_handler);
>
>      engine_add_input(&en_controller_output, &en_dns_cache,
>                       NULL);
> @@ -6771,6 +6886,8 @@ main(int argc, char *argv[])
>          engine_get_internal_data(&en_neighbor_exchange);
>      struct ed_type_evpn_vtep_binding *eb_data =
>          engine_get_internal_data(&en_evpn_vtep_binding);
> +    struct ed_type_evpn_fdb *efdb_data =
> +        engine_get_internal_data(&en_evpn_fdb);
>
>      ofctrl_init(&lflow_output_data->group_table,
>                  &lflow_output_data->meter_table);
> @@ -6796,6 +6913,9 @@ main(int argc, char *argv[])
>      unixctl_command_register("evpn/vtep-multicast-group-list", "", 0, 0,
>                               evpn_multicast_group_list,
>                               &eb_data->multicast_groups);
> +    unixctl_command_register("evpn/vtep-fdb-list", "", 0, 0,
> +                             evpn_fdb_list,
> +                             &efdb_data->fdbs);
>
>      struct pending_pkt pending_pkt = { .conn = NULL };
>      unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt,
> diff --git a/controller/physical.c b/controller/physical.c
> index 0a4b31040..be233cba0 100644
> --- a/controller/physical.c
> +++ b/controller/physical.c
> @@ -20,6 +20,7 @@
>  #include "ct-zone.h"
>  #include "encaps.h"
>  #include "evpn-binding.h"
> +#include "evpn-fdb.h"
>  #include "flow.h"
>  #include "ha-chassis.h"
>  #include "lflow.h"
> @@ -2777,6 +2778,23 @@ physical_consider_evpn_multicast(const struct
> evpn_multicast_group *mc_group,
>      }
>  }
>
> +static void
> +physical_consider_evpn_fdb(const struct evpn_fdb *fdb,
> +                           struct ofpbuf *ofpacts, struct match *match,
> +                           struct ovn_desired_flow_table *flow_table)
> +{
> +    ofpbuf_clear(ofpacts);
> +    match_init_catchall(match);
> +
> +    match_set_metadata(match, htonll(fdb->dp_key));
> +    match_set_dl_dst(match, fdb->mac);
> +
> +    put_load(fdb->binding_key, MFF_LOG_REMOTE_OUTPORT, 0, 32, ofpacts);
> +    ofctrl_add_flow(flow_table, OFTABLE_GET_REMOTE_FDB, 150,
> +                    fdb->flow_uuid.parts[0],
> +                    match, ofpacts, &fdb->flow_uuid);
> +}
> +
>  static void
>  physical_eval_evpn_flows(const struct physical_ctx *ctx,
>                           struct ofpbuf *ofpacts,
> @@ -2803,6 +2821,11 @@ physical_eval_evpn_flows(const struct physical_ctx
> *ctx,
>          physical_consider_evpn_multicast(mc_group, &local_ip, ofpacts,
>                                           &match, flow_table, ipv4);
>      }
> +
> +    const struct evpn_fdb *fdb;
> +    HMAP_FOR_EACH (fdb, hmap_node, ctx->evpn_fdbs) {
> +        physical_consider_evpn_fdb(fdb, ofpacts, &match, flow_table);
> +    }
>  }
>
>  static void
> @@ -2961,6 +2984,31 @@ physical_handle_evpn_binding_changes(
>      }
>  }
>
> +void
> +physical_handle_evpn_fdb_changes(struct ovn_desired_flow_table
> *flow_table,
> +                                 const struct hmapx *updated_fdbs,
> +                                 const struct uuidset *removed_fdbs)
> +{
> +    struct ofpbuf ofpacts;
> +    ofpbuf_init(&ofpacts, 0);
> +    struct match match = MATCH_CATCHALL_INITIALIZER;
> +
> +    const struct hmapx_node *node;
> +    HMAPX_FOR_EACH (node, updated_fdbs) {
> +        const struct evpn_fdb *fdb = node->data;
> +
> +        ofctrl_remove_flows(flow_table, &fdb->flow_uuid);
> +        physical_consider_evpn_fdb(fdb, &ofpacts, &match, flow_table);
> +    }
> +
> +    ofpbuf_uninit(&ofpacts);
> +
> +    const struct uuidset_node *uuidset_node;
> +    UUIDSET_FOR_EACH (uuidset_node, removed_fdbs) {
> +        ofctrl_remove_flows(flow_table, &uuidset_node->uuid);
> +    }
> +}
> +
>  void
>  physical_run(struct physical_ctx *p_ctx,
>               struct ovn_desired_flow_table *flow_table)
> diff --git a/controller/physical.h b/controller/physical.h
> index e0443924b..37be46380 100644
> --- a/controller/physical.h
> +++ b/controller/physical.h
> @@ -71,6 +71,7 @@ struct physical_ctx {
>      bool always_tunnel;
>      const struct hmap *evpn_bindings;
>      const struct hmap *evpn_multicast_groups;
> +    const struct hmap *evpn_fdbs;
>
>      /* Set of port binding names that have been already reprocessed during
>       * the I-P run. */
> @@ -95,4 +96,7 @@ void physical_handle_evpn_binding_changes(
>      const struct hmapx *updated_multicast_groups,
>      const struct uuidset *removed_bindings,
>      const struct uuidset *removed_multicast_groups);
> +void physical_handle_evpn_fdb_changes(struct ovn_desired_flow_table *,
> +                                      const struct hmapx *updated_fdbs,
> +                                      const struct uuidset *removed_fdbs);
>  #endif /* controller/physical.h */
> diff --git a/tests/ovn-macros.at b/tests/ovn-macros.at
> index d6529e015..25b3fb68d 100644
> --- a/tests/ovn-macros.at
> +++ b/tests/ovn-macros.at
> @@ -1502,5 +1502,6 @@ m4_define([OFTABLE_CT_ORIG_TP_DST_LOAD], [83])
>  m4_define([OFTABLE_FLOOD_REMOTE_CHASSIS], [84])
>  m4_define([OFTABLE_CT_STATE_SAVE], [85])
>  m4_define([OFTABLE_CT_ORIG_PROTO_LOAD], [86])
> +m4_define([OFTABLE_GET_REMOTE_FDB], [87])
>
>  m4_define([OFTABLE_SAVE_INPORT_HEX], [m4_eval(OFTABLE_SAVE_INPORT, 16)])
> --
> 2.50.0
>
>
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to