On 8/12/25 4:56 PM, Ales Musil via dev wrote: > Pair learned remote VTEPs with their respective datapaths and tunnel > ports. This creates binding that can be used for physical flows. In > order to accommodate for multicast groups we need to also create > specific EVPN multicast groups. > > All data are ovn-controller specific due to the nature of EVPN and > also to avoid possible scalability issues within SB database. > > Those bindings and multicast groups are used in later commits to > create physical flows responsible for the delivery. > > Note that there isn't any way to set vni for given LS that will > be part of future patch ("dynamic-routing-vni"). > > Signed-off-by: Ales Musil <amu...@redhat.com> > --- > v3: Rebase on top of latest main. > Rename "tunnel_key" to "binding_key". > Adjust the tunnel search to use the tunnels created by ovn. > > v2: Rebase on top of latest main. > Fix small memory leak that was present in multicast groups. > Address nits from Dumitru. > --- > controller/automake.mk | 2 + > controller/evpn-binding.c | 416 ++++++++++++++++++++++++++++++++++++ > controller/evpn-binding.h | 88 ++++++++ > controller/ovn-controller.c | 201 ++++++++++++++++- > lib/ovn-util.h | 3 + > 5 files changed, 699 insertions(+), 11 deletions(-) > create mode 100644 controller/evpn-binding.c > create mode 100644 controller/evpn-binding.h > > diff --git a/controller/automake.mk b/controller/automake.mk > index 37cfd4396..8d94fb646 100644 > --- a/controller/automake.mk > +++ b/controller/automake.mk > @@ -10,6 +10,8 @@ controller_ovn_controller_SOURCES = \ > controller/chassis.h \ > controller/encaps.c \ > controller/encaps.h \ > + controller/evpn-binding.c \ > + controller/evpn-binding.h \ > controller/ha-chassis.c \ > controller/ha-chassis.h \ > controller/if-status.c \ > diff --git a/controller/evpn-binding.c b/controller/evpn-binding.c > new file mode 100644 > index 000000000..0978a9982 > --- /dev/null > +++ b/controller/evpn-binding.c > @@ -0,0 +1,416 @@ > +/* 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 "flow.h" > +#include "local_data.h" > +#include "neighbor-exchange.h" > +#include "openvswitch/vlog.h" > +#include "ovn-sb-idl.h" > +#include "unixctl.h" > +#include "vec.h" > +#include "vswitch-idl.h" > + > +#include "evpn-binding.h" > + > +VLOG_DEFINE_THIS_MODULE(evpn_binding); > + > +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); > + > +struct evpn_datapath { > + uint32_t vni; > + uint32_t dp_key; > +}; > + > +static struct vector collect_evpn_datapaths( > + const struct hmap *local_datapaths); > +static const struct evpn_datapath *evpn_datapath_find( > + const struct vector *evpn_datapaths, uint32_t vni); > + > +struct evpn_tunnel { > + uint16_t dst_port; > + ofp_port_t ofport; > +}; > + > +static struct vector collect_evpn_tunnel_interfaces( > + const struct ovsrec_bridge *br_int); > +static const struct evpn_tunnel *evpn_tunnel_find( > + const struct vector *evpn_tunnels, uint16_t dst_port); > + > +static struct evpn_binding *evpn_binding_add( > + struct hmap *evpn_bindings, const struct evpn_remote_vtep *vtep, > + uint32_t binding_key); > +static uint32_t evpn_binding_hash(const struct in6_addr *remote_ip, > + uint32_t vni); > +static struct evpn_multicast_group *evpn_multicast_group_find( > + const struct hmap *evpn_mc_groups, uint32_t vni); > +static struct evpn_multicast_group *evpn_multicast_group_add( > + struct hmap *evpn_mc_groups, uint32_t vni); > + > +void > +evpn_binding_run(const struct evpn_binding_ctx_in *b_ctx_in, > + struct evpn_binding_ctx_out *b_ctx_out) > +{ > + struct vector datapaths = > + collect_evpn_datapaths(b_ctx_in->local_datapaths); > + struct vector tunnels = collect_evpn_tunnel_interfaces(b_ctx_in->br_int); > + struct hmapx stale_bindings = HMAPX_INITIALIZER(&stale_bindings); > + struct hmapx stale_mc_groups = HMAPX_INITIALIZER(&stale_mc_groups); > + uint32_t hint = OVN_MIN_EVPN_KEY; > + > + struct evpn_binding *binding; > + HMAP_FOR_EACH (binding, hmap_node, b_ctx_out->bindings) { > + hmapx_add(&stale_bindings, binding); > + } > + > + struct evpn_multicast_group *mc_group; > + HMAP_FOR_EACH (mc_group, hmap_node, b_ctx_out->multicast_groups) { > + hmapx_add(&stale_mc_groups, mc_group); > + } > + > + const struct evpn_remote_vtep *vtep; > + HMAP_FOR_EACH (vtep, hmap_node, b_ctx_in->remote_vteps) { > + const struct evpn_tunnel *tun = evpn_tunnel_find(&tunnels, > vtep->port); > + if (!tun) { > + VLOG_WARN_RL(&rl, "Couldn't find EVPN tunnel for %"PRIu16 > + " destination port.", vtep->port); > + continue; > + } > + > + const struct evpn_datapath *edp = evpn_datapath_find(&datapaths, > + vtep->vni); > + if (!edp) { > + VLOG_WARN_RL(&rl, "Couldn't find EVPN datapath for %"PRIu16" > VNI", > + vtep->vni); > + continue; > + } > + > + binding = evpn_binding_find(b_ctx_out->bindings, &vtep->ip, > vtep->vni); > + if (!binding) { > + uint32_t tunnel_key = > + ovn_allocate_tnlid(b_ctx_out->tunnel_keys, "evpn-binding", > + OVN_MIN_EVPN_KEY, OVN_MAX_EVPN_KEY, > &hint); > + if (!tunnel_key) { > + continue; > + } > + > + binding = evpn_binding_add(b_ctx_out->bindings, vtep, > tunnel_key); > + } > + > + mc_group = evpn_multicast_group_find(b_ctx_out->multicast_groups, > + vtep->vni); > + if (!mc_group) { > + mc_group = evpn_multicast_group_add(b_ctx_out->multicast_groups, > + vtep->vni); > + } > + > + bool updated = false; > + if (binding->tunnel_ofport != tun->ofport) { > + binding->tunnel_ofport = tun->ofport; > + updated = true; > + } > + > + if (binding->dp_key != edp->dp_key) { > + binding->dp_key = edp->dp_key; > + updated = true; > + } > + > + if (updated) { > + hmapx_add(b_ctx_out->updated_bindings, binding); > + > + hmapx_add(&mc_group->bindings, binding); > + hmapx_add(b_ctx_out->updated_multicast_groups, mc_group); > + } > + > + hmapx_find_and_delete(&stale_bindings, binding); > + hmapx_find_and_delete(&stale_mc_groups, mc_group); > + } > + > + struct hmapx_node *node; > + HMAPX_FOR_EACH (node, &stale_mc_groups) { > + mc_group = node->data; > + > + uuidset_insert(b_ctx_out->removed_multicast_groups, > + &mc_group->flow_uuid); > + hmap_remove(b_ctx_out->multicast_groups, &mc_group->hmap_node); > + free(mc_group); > + } > + > + HMAPX_FOR_EACH (node, &stale_bindings) { > + binding = node->data; > + > + mc_group = evpn_multicast_group_find(b_ctx_out->multicast_groups, > + binding->vni); > + if (mc_group) { > + hmapx_find_and_delete(&mc_group->bindings, binding); > + hmapx_add(b_ctx_out->updated_multicast_groups, mc_group); > + } > + uuidset_insert(b_ctx_out->removed_bindings, &binding->flow_uuid); > + hmap_remove(b_ctx_out->bindings, &binding->hmap_node); > + free(binding); > + } > + > + vector_destroy(&datapaths); > + vector_destroy(&tunnels); > + hmapx_destroy(&stale_bindings); > + hmapx_destroy(&stale_mc_groups); > +} > + > +struct evpn_binding * > +evpn_binding_find(const struct hmap *evpn_bindings, > + const struct in6_addr *remote_ip, uint32_t vni) > +{ > + uint32_t hash = evpn_binding_hash(remote_ip, vni); > + > + struct evpn_binding *binding; > + HMAP_FOR_EACH_WITH_HASH (binding, hmap_node, hash, evpn_bindings) { > + if (ipv6_addr_equals(&binding->remote_ip, remote_ip) && > + binding->vni == vni) { > + return binding; > + } > + } > + > + return NULL; > +} > + > +void > +evpn_bindings_destroy(struct hmap *bindings) > +{ > + struct evpn_binding *binding; > + HMAP_FOR_EACH_POP (binding, hmap_node, bindings) { > + free(binding); > + } > + hmap_destroy(bindings); > +} > + > +void > +evpn_vtep_binding_list(struct unixctl_conn *conn, int argc OVS_UNUSED, > + const char *argv[] OVS_UNUSED, void *data_) > +{ > + struct hmap *bindings = data_; > + struct ds ds = DS_EMPTY_INITIALIZER; > + > + const struct evpn_binding *binding; > + HMAP_FOR_EACH (binding, hmap_node, bindings) { > + ds_put_format(&ds, "UUID: "UUID_FMT", Remote IP: ", > + UUID_ARGS(&binding->flow_uuid)); > + ipv6_format_mapped(&binding->remote_ip, &ds); > + ds_put_format(&ds, ", vni: %"PRIu32", binding_key: %#"PRIx32", " > + "tunnel_ofport: %"PRIu32", dp_key: %"PRIu32, > + binding->vni, binding->binding_key, > + binding->tunnel_ofport, binding->dp_key); > + ds_put_char(&ds, '\n'); > + } > + > + unixctl_command_reply(conn, ds_cstr_ro(&ds)); > + ds_destroy(&ds); > +} > + > +void > +evpn_multicast_groups_destroy(struct hmap *multicast_groups) > +{ > + struct evpn_multicast_group *mc_group; > + HMAP_FOR_EACH_POP (mc_group, hmap_node, multicast_groups) { > + hmapx_destroy(&mc_group->bindings); > + free(mc_group); > + } > + hmap_destroy(multicast_groups); > +} > + > + > +void > +evpn_multicast_group_list(struct unixctl_conn *conn, int argc OVS_UNUSED, > + const char *argv[] OVS_UNUSED, void *data_) > +{ > + struct hmap *mc_groups = data_; > + struct ds ds = DS_EMPTY_INITIALIZER; > + > + const struct evpn_multicast_group *mc_group; > + HMAP_FOR_EACH (mc_group, hmap_node, mc_groups) { > + ds_put_format(&ds, "UUID: "UUID_FMT", Remote IPs: ", > + UUID_ARGS(&mc_group->flow_uuid)); > + > + struct hmapx_node *node; > + HMAPX_FOR_EACH (node, &mc_group->bindings) { > + const struct evpn_binding *binding = node->data; > + ipv6_format_mapped(&binding->remote_ip, &ds); > + ds_put_cstr(&ds, ", "); > + } > + > + ds_put_format(&ds, "vni: %"PRIu32, mc_group->vni); > + ds_put_char(&ds, '\n'); > + } > + > + unixctl_command_reply(conn, ds_cstr_ro(&ds)); > + ds_destroy(&ds); > +} > + > +static struct vector > +collect_evpn_datapaths(const struct hmap *local_datapaths) > +{ > + struct vector evpn_datapaths = > + VECTOR_EMPTY_INITIALIZER(struct evpn_datapath); > + > + struct local_datapath *ld; > + HMAP_FOR_EACH (ld, hmap_node, local_datapaths) { > + if (!ld->is_switch) { > + continue; > + } > + > + int64_t vni = ovn_smap_get_llong(&ld->datapath->external_ids, > + "dynamic-routing-vni", -1); > + if (!ovn_is_valid_vni(vni)) { > + continue; > + } > + > + if (evpn_datapath_find(&evpn_datapaths, vni)) { > + VLOG_WARN_RL(&rl, "Datapath "UUID_FMT" with duplicate VNI > %"PRIi64, > + UUID_ARGS(&ld->datapath->header_.uuid), vni); > + continue; > + } > + > + struct evpn_datapath edp = { > + .vni = vni, > + .dp_key = ld->datapath->tunnel_key, > + }; > + vector_push(&evpn_datapaths, &edp); > + } > + > + return evpn_datapaths; > +} > + > +static const struct evpn_datapath * > +evpn_datapath_find(const struct vector *evpn_datapaths, uint32_t vni) > +{ > + const struct evpn_datapath *edp; > + VECTOR_FOR_EACH_PTR (evpn_datapaths, edp) { > + if (edp->vni == vni) { > + return edp; > + } > + } > + > + return NULL; > +} > + > +static struct vector > +collect_evpn_tunnel_interfaces(const struct ovsrec_bridge *br_int) > +{ > + struct vector evpn_tunnels = VECTOR_EMPTY_INITIALIZER(struct > evpn_tunnel); > + > + for (size_t i = 0; i < br_int->n_ports; i++) { > + const struct ovsrec_port *port = br_int->ports[i]; > + if (!smap_get_bool(&port->external_ids, "ovn-evpn-tunnel", false)) { > + continue; > + } > + > + const struct ovsrec_interface *iface = port->interfaces[0]; > + if (iface->n_ofport != 1) { > + continue; > + } > + > + const char *dst_port_str = > + smap_get_def(&iface->options, "dst_port", > + OVS_STRINGIZE(DEFAULT_VXLAN_PORT)); > + unsigned int dst_port; > + if (!str_to_uint(dst_port_str, 10, &dst_port)) { > + VLOG_WARN_RL(&rl, "Couldn't parse \"dst_port\" %s for tunnel" > + " interface %s", dst_port_str, iface->name); > + continue; > + } > + > + struct evpn_tunnel tun = { > + .dst_port = dst_port, > + .ofport = u16_to_ofp(iface->ofport[0]), > + }; > + vector_push(&evpn_tunnels, &tun); > + } > + > + return evpn_tunnels; > +} > + > +static const struct evpn_tunnel * > +evpn_tunnel_find(const struct vector *evpn_tunnels, uint16_t dst_port) > +{ > + const struct evpn_tunnel *tun; > + VECTOR_FOR_EACH_PTR (evpn_tunnels, tun) { > + if (tun->dst_port == dst_port) { > + return tun; > + } > + } > + > + return NULL; > +} > + > +static uint32_t > +evpn_binding_hash(const struct in6_addr *remote_ip, uint32_t vni) > +{ > + uint32_t hash = 0; > + hash = hash_add_in6_addr(hash, remote_ip); > + hash = hash_add(hash, vni); > + > + return hash;
hash_finish(hash, vni); > +} > + > +static struct evpn_binding * > +evpn_binding_add(struct hmap *evpn_bindings, > + const struct evpn_remote_vtep *vtep, uint32_t binding_key) > +{ > + struct evpn_binding *binding = xmalloc(sizeof *binding); > + *binding = (struct evpn_binding) { > + .flow_uuid = uuid_random(), > + .remote_ip = vtep->ip, > + .vni = vtep->vni, > + .binding_key = binding_key, > + .tunnel_ofport = OFPP_NONE, > + .dp_key = 0, > + }; > + > + uint32_t hash = evpn_binding_hash(&vtep->ip, vtep->vni); > + hmap_insert(evpn_bindings, &binding->hmap_node, hash); > + > + return binding; > +} > + > +static struct evpn_multicast_group * > +evpn_multicast_group_find(const struct hmap *evpn_mc_groups, uint32_t vni) > +{ > + uint32_t hash = hash_add(0, vni); > + > + struct evpn_multicast_group *mc_group; > + HMAP_FOR_EACH_WITH_HASH (mc_group, hmap_node, hash, evpn_mc_groups) { > + if (mc_group->vni == vni) { > + return mc_group; > + } > + } > + > + return NULL; > +} > + > +static struct evpn_multicast_group * > +evpn_multicast_group_add(struct hmap *evpn_mc_groups, uint32_t vni) > +{ > + struct evpn_multicast_group *mc_group = xmalloc(sizeof *mc_group); > + *mc_group = (struct evpn_multicast_group) { > + .flow_uuid = uuid_random(), > + .bindings = HMAPX_INITIALIZER(&mc_group->bindings), > + .vni = vni, > + }; > + > + uint32_t hash = hash_add(0, vni); hash_int(vni, 42); > + hmap_insert(evpn_mc_groups, &mc_group->hmap_node, hash); > + > + return mc_group; > +} > diff --git a/controller/evpn-binding.h b/controller/evpn-binding.h > new file mode 100644 > index 000000000..229d29b09 > --- /dev/null > +++ b/controller/evpn-binding.h > @@ -0,0 +1,88 @@ > +/* 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_BINDING_H > +#define EVPN_BINDING_H 1 > + > +#include <stdint.h> > + > +#include "hmapx.h" > +#include "openvswitch/hmap.h" > +#include "uuidset.h" > + > +struct ovsrec_bridge; > +struct unixctl_conn; > + > +struct evpn_binding_ctx_in { > + const struct ovsrec_bridge *br_int; > + /* Contains 'struct local_datapath'. */ > + const struct hmap *local_datapaths; > + /* Contains 'struct evpn_remote_vtep'. */ > + const struct hmap *remote_vteps; > +}; > + > +struct evpn_binding_ctx_out { > + /* Contains 'struct evpn_binding'. */ > + struct hmap *bindings; > + /* Contains pointers to 'struct evpn_binding'. */ > + struct hmapx *updated_bindings; > + /* Contains 'flow_uuid' from removed 'struct evpn_binding'. */ > + struct uuidset *removed_bindings; > + /* Contains 'struct evpn_multicast_group'. */ > + struct hmap *multicast_groups; > + /* Contains pointers to 'struct evpn_multicast_group'. */ > + struct hmapx *updated_multicast_groups; > + /* Contains 'flow_uuid' from removed 'struct evpn_multicast_group'. */ > + struct uuidset *removed_multicast_groups; > + /* Contains 'struct tnlid_node". */ > + struct hmap *tunnel_keys; > +}; > + > +struct evpn_binding { > + struct hmap_node hmap_node; > + /* UUID used to identify physical flows related to this binding. */ > + struct uuid flow_uuid; > + /* IP address of the remote VTEP. */ > + struct in6_addr remote_ip; > + uint32_t vni; > + /* Local tunnel key to identify the binding. */ > + uint32_t binding_key; > + > + ofp_port_t tunnel_ofport; > + uint32_t dp_key; > +}; > + > +struct evpn_multicast_group { > + struct hmap_node hmap_node; > + /* UUID used to identify physical flows related to this mutlicast group. > */ > + struct uuid flow_uuid; > + /* Contains pointers to 'struct evpn_bindings'. */ > + struct hmapx bindings; > + uint32_t vni; > +}; > + > +void evpn_binding_run(const struct evpn_binding_ctx_in *, > + struct evpn_binding_ctx_out *); > +struct evpn_binding *evpn_binding_find(const struct hmap *evpn_bindings, > + const struct in6_addr *remote_ip, > + uint32_t vni); > +void evpn_bindings_destroy(struct hmap *bindings); > +void evpn_vtep_binding_list(struct unixctl_conn *conn, int argc, > + const char *argv[], void *data_); > +void evpn_multicast_groups_destroy(struct hmap *multicast_groups); > +void evpn_multicast_group_list(struct unixctl_conn *conn, int argc, > + const char *argv[], void *data_); > + > +#endif /* EVPN_BINDING_H */ > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c > index 278496c24..1cda8e71e 100644 > --- a/controller/ovn-controller.c > +++ b/controller/ovn-controller.c > @@ -98,6 +98,7 @@ > #include "neighbor.h" > #include "neighbor-exchange.h" > #include "neighbor-table-notify.h" > +#include "evpn-binding.h" > > VLOG_DEFINE_THIS_MODULE(main); > > @@ -4558,6 +4559,23 @@ parse_encap_ips(const struct ovsrec_open_vswitch_table > *ovs_table, > } > } > > +struct ed_type_evpn_vtep_binding { > + /* Contains 'struct evpn_binding'. */ > + struct hmap bindings; > + /* Contains pointers to 'struct evpn_binding'. */ > + struct hmapx updated_bindings; > + /* Contains 'flow_uuid' from removed 'struct evpn_binding'. */ > + struct uuidset removed_bindings; > + /* Contains 'struct evpn_multicast_group'. */ > + struct hmap multicast_groups; > + /* Contains pointers to 'struct evpn_multicast_group'. */ > + struct hmapx updated_multicast_groups; > + /* Contains 'flow_uuid' from removed 'struct evpn_multicast_group'. */ > + struct uuidset removed_multicast_groups; > + /* Contains 'struct tnlid_node". */ > + struct hmap tunnel_keys; > +}; > + > 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, > @@ -4988,14 +5006,6 @@ controller_output_garp_rarp_handler(struct engine_node > *node OVS_UNUSED, > return EN_HANDLED_UPDATED; > } > > -static enum engine_input_handler_result > -controller_output_neighbor_exchange_handler( > - struct engine_node *node OVS_UNUSED, > - void *data OVS_UNUSED) > -{ > - return EN_HANDLED_UPDATED; > -} > - > /* Handles sbrec_chassis changes. > * If a new chassis is added or removed return false, so that > * flows are recomputed. For any updates, there is no need for > @@ -5943,6 +5953,153 @@ en_neighbor_exchange_status_run(struct engine_node > *node OVS_UNUSED, > return state; > } > > +static void * > +en_evpn_vtep_binding_init(struct engine_node *node OVS_UNUSED, > + struct engine_arg *arg OVS_UNUSED) > +{ > + struct ed_type_evpn_vtep_binding *data = xmalloc(sizeof *data); > + *data = (struct ed_type_evpn_vtep_binding) { > + .bindings = HMAP_INITIALIZER(&data->bindings), > + .updated_bindings = HMAPX_INITIALIZER(&data->updated_bindings), > + .removed_bindings = UUIDSET_INITIALIZER(&data->removed_bindings), > + .multicast_groups = HMAP_INITIALIZER(&data->multicast_groups), > + .updated_multicast_groups = > + HMAPX_INITIALIZER(&data->updated_multicast_groups), > + .removed_multicast_groups = > + UUIDSET_INITIALIZER(&data->removed_multicast_groups), > + .tunnel_keys = HMAP_INITIALIZER(&data->tunnel_keys), > + }; > + > + return data; > +} > + > +static void > +en_evpn_vtep_binding_clear_tracked_data(void *data_) > +{ > + struct ed_type_evpn_vtep_binding *data = data_; > + hmapx_clear(&data->updated_bindings); > + uuidset_clear(&data->removed_bindings); > + hmapx_clear(&data->updated_multicast_groups); > + uuidset_clear(&data->removed_multicast_groups); > +} > + > +static void > +en_evpn_vtep_binding_cleanup(void *data_) > +{ > + struct ed_type_evpn_vtep_binding *data = data_; > + evpn_bindings_destroy(&data->bindings); > + hmapx_destroy(&data->updated_bindings); > + uuidset_destroy(&data->removed_bindings); > + evpn_multicast_groups_destroy(&data->multicast_groups); > + hmapx_clear(&data->updated_multicast_groups); > + uuidset_clear(&data->removed_multicast_groups); > + ovn_destroy_tnlids(&data->tunnel_keys); > +} > + > +static enum engine_node_state > +en_evpn_vtep_binding_run(struct engine_node *node, void *data_) > +{ > + struct ed_type_evpn_vtep_binding *data = data_; > + const struct ed_type_neighbor_exchange *ne_data = > + engine_get_input_data("neighbor_exchange", node); > + const struct ed_type_runtime_data *rt_data = > + engine_get_input_data("runtime_data", node); > + const struct ovsrec_open_vswitch_table *ovs_table = > + EN_OVSDB_GET(engine_get_input("OVS_open_vswitch", node)); > + const struct ovsrec_bridge_table *bridge_table = > + EN_OVSDB_GET(engine_get_input("OVS_bridge", node)); > + const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table); > + > + struct evpn_binding_ctx_in b_ctx_in = { > + .br_int = br_int, > + .local_datapaths = &rt_data->local_datapaths, > + .remote_vteps = &ne_data->remote_vteps, > + }; > + > + struct evpn_binding_ctx_out b_ctx_out = { > + .bindings = &data->bindings, > + .updated_bindings = &data->updated_bindings, > + .removed_bindings = &data->removed_bindings, > + .multicast_groups = &data->multicast_groups, > + .updated_multicast_groups = &data->updated_multicast_groups, > + .removed_multicast_groups = &data->removed_multicast_groups, > + .tunnel_keys = &data->tunnel_keys, > + }; > + > + evpn_binding_run(&b_ctx_in, &b_ctx_out); > + > + if (hmapx_count(&data->updated_bindings) || > + uuidset_count(&data->removed_bindings) || > + hmapx_count(&data->updated_multicast_groups) || > + uuidset_count(&data->removed_multicast_groups)) { > + return EN_UPDATED; > + } > + > + return EN_UNCHANGED; > +} > + > +static enum engine_input_handler_result > +evpn_vtep_binding_ovs_interface_handler(struct engine_node *node, > + void *data OVS_UNUSED) > +{ > + const struct ovsrec_interface_table *iface_table = > + EN_OVSDB_GET(engine_get_input("OVS_interface", node)); > + > + const struct ovsrec_interface *iface; > + OVSREC_INTERFACE_TABLE_FOR_EACH_TRACKED (iface, iface_table) { > + if (!smap_get_bool(&iface->external_ids, "ovn-evpn-tunnel", false)) { > + continue; > + } > + > + if (ovsrec_interface_is_new(iface) || > + ovsrec_interface_is_deleted(iface) || > + ovsrec_interface_is_updated(iface, OVSREC_INTERFACE_COL_OFPORT)) > { > + return EN_UNHANDLED; > + } > + } > + > + return EN_HANDLED_UNCHANGED; > +} > + > +static enum engine_input_handler_result > +evpn_vtep_binding_datapath_binding_handler(struct engine_node *node, > + void *data OVS_UNUSED) > +{ > + 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 = > + engine_get_input_data("runtime_data", node); > + > + const struct sbrec_datapath_binding *dp; > + SBREC_DATAPATH_BINDING_TABLE_FOR_EACH_TRACKED (dp, dp_table) { > + if (sbrec_datapath_binding_is_new(dp) || > + sbrec_datapath_binding_is_deleted(dp)) { > + /* The removal and addition is handled via the > + * en_neighbor_exchange I-P node. */ > + return EN_HANDLED_UNCHANGED; > + } > + > + struct local_datapath *ld = > + get_local_datapath(&rt_data->local_datapaths, dp->tunnel_key); > + if (!ld || !ld->is_switch) { > + continue; > + } > + > + int64_t vni = ovn_smap_get_llong(&dp->external_ids, > + "dynamic-routing-vni", -1); > + if (!ovn_is_valid_vni(vni)) { > + continue; > + } > + > + if (sbrec_datapath_binding_is_updated( > + dp, SBREC_DATAPATH_BINDING_COL_TUNNEL_KEY)) { > + return EN_UNHANDLED; > + } > + } > + > + return EN_HANDLED_UNCHANGED; > +} > + > /* Returns false if the northd internal version stored in SB_Global > * and ovn-controller internal version don't match. > */ > @@ -6267,6 +6424,7 @@ main(int argc, char *argv[]) > ENGINE_NODE(neighbor_table_notify); > ENGINE_NODE(neighbor_exchange); > ENGINE_NODE(neighbor_exchange_status); > + ENGINE_NODE(evpn_vtep_binding, CLEAR_TRACKED_DATA); > > #define SB_NODE(NAME) ENGINE_NODE_SB(NAME); > SB_NODES > @@ -6502,6 +6660,22 @@ main(int argc, char *argv[]) > engine_add_input(&en_neighbor_exchange, &en_neighbor_exchange_status, > NULL); > > + engine_add_input(&en_evpn_vtep_binding, &en_ovs_open_vswitch, NULL); > + engine_add_input(&en_evpn_vtep_binding, &en_ovs_bridge, NULL); > + engine_add_input(&en_evpn_vtep_binding, &en_neighbor_exchange, NULL); > + /* The runtime_data are needed only for local datapaths, any update of > + * local datapath will be reflected via en_neighbor_exchange. */ > + engine_add_input(&en_evpn_vtep_binding, &en_runtime_data, > + engine_noop_handler); > + engine_add_input(&en_evpn_vtep_binding, &en_ovs_interface, > + evpn_vtep_binding_ovs_interface_handler); > + engine_add_input(&en_evpn_vtep_binding, &en_sb_datapath_binding, > + evpn_vtep_binding_datapath_binding_handler); > + > + /* TODO Add real handler to process all bindings. */ > + engine_add_input(&en_pflow_output, &en_evpn_vtep_binding, > + engine_noop_handler); > + > engine_add_input(&en_controller_output, &en_dns_cache, > NULL); > engine_add_input(&en_controller_output, &en_lflow_output, > @@ -6521,9 +6695,6 @@ main(int argc, char *argv[]) > engine_add_input(&en_controller_output, &en_acl_id, > controller_output_acl_id_handler); > > - engine_add_input(&en_controller_output, &en_neighbor_exchange, > - controller_output_neighbor_exchange_handler); > - > struct engine_arg engine_arg = { > .sb_idl = ovnsb_idl_loop.idl, > .ovs_idl = ovs_idl_loop.idl, > @@ -6583,6 +6754,8 @@ main(int argc, char *argv[]) > engine_get_internal_data(&en_mac_cache); > struct ed_type_neighbor_exchange *ne_data = > engine_get_internal_data(&en_neighbor_exchange); > + struct ed_type_evpn_vtep_binding *eb_data = > + engine_get_internal_data(&en_evpn_vtep_binding); > > ofctrl_init(&lflow_output_data->group_table, > &lflow_output_data->meter_table); > @@ -6602,6 +6775,12 @@ main(int argc, char *argv[]) > unixctl_command_register("evpn/remote-vtep-list", "", 0, 0, > evpn_remote_vtep_list, > &ne_data->remote_vteps); > + unixctl_command_register("evpn/vtep-binding-list", "", 0, 0, > + evpn_vtep_binding_list, > + &eb_data->bindings); > + unixctl_command_register("evpn/vtep-multicast-group-list", "", 0, 0, > + evpn_multicast_group_list, > + &eb_data->multicast_groups); > > struct pending_pkt pending_pkt = { .conn = NULL }; > unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt, > diff --git a/lib/ovn-util.h b/lib/ovn-util.h > index e8c2cedc5..fdb845bfd 100644 > --- a/lib/ovn-util.h > +++ b/lib/ovn-util.h > @@ -168,6 +168,9 @@ void set_idl_probe_interval(struct ovsdb_idl *idl, const > char *remote, > #define OVN_MIN_DP_VXLAN_KEY_GLOBAL (OVN_MAX_DP_VXLAN_KEY_LOCAL + 1) > #define OVN_MAX_DP_VXLAN_KEY_GLOBAL ((1u << 12) - 1) > > +#define OVN_MIN_EVPN_KEY (1u << 31) > +#define OVN_MAX_EVPN_KEY (OVN_MAX_DP_GLOBAL_NUM | OVN_MIN_EVPN_KEY) > + > struct hmap; > void ovn_destroy_tnlids(struct hmap *tnlids); > bool ovn_add_tnlid(struct hmap *set, uint32_t tnlid); _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev