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); _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev