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> --- v3: Rebase on top of latest main. v2: Rebase on top of latest main. Add missing MFF_LOG_REMOTE_OUTPORT that was defined in the next commit wrongfully. --- TODO.rst | 3 + controller/automake.mk | 2 + controller/evpn-fdb.c | 151 +++++++++++++++++++++++++ controller/evpn-fdb.h | 59 ++++++++++ controller/lflow.h | 1 + controller/neighbor-exchange-netlink.c | 9 ++ controller/neighbor-exchange-netlink.h | 1 + controller/neighbor-exchange-stub.c | 5 + controller/neighbor-exchange.c | 69 ++++++++++- controller/neighbor-exchange.h | 13 +++ controller/ovn-controller.c | 122 +++++++++++++++++++- controller/physical.c | 51 ++++++++- controller/physical.h | 4 + tests/ovn-macros.at | 1 + 14 files changed, 488 insertions(+), 3 deletions(-) create mode 100644 controller/evpn-fdb.c create mode 100644 controller/evpn-fdb.h diff --git a/TODO.rst b/TODO.rst index 9a9df78f2..0d3c29506 100644 --- a/TODO.rst +++ b/TODO.rst @@ -157,3 +157,6 @@ 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/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..53312fbb2 --- /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->binding_key) { + fdb->binding_key = binding->binding_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-netlink.c b/controller/neighbor-exchange-netlink.c index 593e01dd0..f0f1f44cc 100644 --- a/controller/neighbor-exchange-netlink.c +++ b/controller/neighbor-exchange-netlink.c @@ -147,6 +147,15 @@ ne_is_valid_remote_vtep(struct ne_nl_received_neigh *ne) (ne->state & NUD_PERMANENT); } +/* OVN expects that the FDB entry doesn't have any IP address (zeroed out), + * has MAC address and the entry is marked as "extern learned". */ +bool +ne_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; +} + static bool ne_table_dump_one_ifindex(unsigned char address_family, int32_t if_index, ne_table_handle_msg_callback *handle_msg_cb, diff --git a/controller/neighbor-exchange-netlink.h b/controller/neighbor-exchange-netlink.h index 557367fdc..ee04691eb 100644 --- a/controller/neighbor-exchange-netlink.h +++ b/controller/neighbor-exchange-netlink.h @@ -55,6 +55,7 @@ int ne_nl_sync_neigh(uint8_t family, int32_t if_index, bool ne_is_ovn_owned(const struct ne_nl_received_neigh *nd); bool ne_is_valid_remote_vtep(struct ne_nl_received_neigh *ne); +bool ne_is_valid_static_fdb(struct ne_nl_received_neigh *ne); int ne_table_parse(struct ofpbuf *, void *change); 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 4d9ca8b3c..1b657665d 100644 --- a/controller/neighbor-exchange.c +++ b/controller/neighbor-exchange.c @@ -37,7 +37,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, const 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; @@ -92,6 +98,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 (ne_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); + } } } } @@ -136,6 +149,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) @@ -180,3 +202,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 603ec3f87..6122d8dee 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 *, 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 86cf5f537..b3199f29b 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; @@ -6127,6 +6160,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. */ @@ -6452,6 +6560,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 @@ -6699,8 +6808,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); @@ -6782,6 +6897,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); @@ -6807,6 +6924,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 78cd98b61..a2b8c4071 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" @@ -2760,13 +2761,31 @@ 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, struct ovn_desired_flow_table *flow_table) { if (hmap_is_empty(ctx->evpn_bindings) && - hmap_is_empty(ctx->evpn_multicast_groups)) { + hmap_is_empty(ctx->evpn_multicast_groups) && + hmap_is_empty(ctx->evpn_fdbs)) { return; } @@ -2794,6 +2813,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 @@ -2952,6 +2976,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 d82c98e35..f41edbb16 100644 --- a/tests/ovn-macros.at +++ b/tests/ovn-macros.at @@ -1506,5 +1506,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.1 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev