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

Reply via email to