On Thu, Aug 14, 2025 at 3:10 PM Ilya Maximets <i.maxim...@ovn.org> wrote:
> On 8/12/25 4:56 PM, Ales Musil via dev 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> > > --- > > 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; > > hash_finish(hash, vni); > > Hi Ilya, thank you for the review. I have addressed this in v4. Thanks, Ales _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev