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