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

Reply via email to