On Tue, Aug 12, 2025 at 3:06 PM Xavier Simonart <xsimo...@redhat.com> wrote:
> Hi Ales, Dumitru > > Thanks for the patch > I only have one nit below > Acked-by: Xavier Simonart <xsimo...@redhat.com> > Thanks > Xavier > Hi Xavier, thank you for the review. > > On Wed, Aug 6, 2025 at 8:29 PM Ales Musil via dev <ovs-dev@openvswitch.org> > wrote: > >> On Wed, Aug 6, 2025 at 12:53 PM Dumitru Ceara <dce...@redhat.com> wrote: >> >> > Hi Ales, >> > >> >> Hi Dumitru, >> >> thank you for the review. >> >> > >> > On 7/25/25 8:03 AM, Ales Musil 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. >> > >> > Maybe just add a small note here too (like in 05/11) that the >> > "dynamic-routing-vni" cannot be set through the NB yet and will be part >> > of a later commit? >> > >> > Otherwise, I only have a few tiny comments below, the rest looks good to >> > me. >> > >> > > >> > > 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. >> > > >> > > Signed-off-by: Ales Musil <amu...@redhat.com> >> > > --- >> > > controller/automake.mk | 2 + >> > > controller/evpn-binding.c | 427 >> ++++++++++++++++++++++++++++++++++++ >> > > controller/evpn-binding.h | 88 ++++++++ >> > > controller/ovn-controller.c | 200 ++++++++++++++++- >> > > lib/ovn-util.c | 16 ++ >> > > lib/ovn-util.h | 5 + >> > > 6 files changed, 727 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..86cb7df27 >> > > --- /dev/null >> > > +++ b/controller/evpn-binding.c >> > > @@ -0,0 +1,427 @@ >> > > +/* 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 bool is_evpn_tunnel_port(const struct ovsrec_port *port); >> > >> > Nit: 'port' can be omitted. >> > >> > > +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 tunnel_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", tunnel_key: >> %#"PRIx32", " >> > > + "ofport: %"PRIu32", dp_key: %"PRIu32, >> > binding->vni, >> > > + binding->tunnel_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) { >> > > + 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 (!is_evpn_tunnel_port(port)) { >> > > + 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 bool >> > > +is_evpn_tunnel_port(const struct ovsrec_port *port) >> > > +{ >> > > + if (port->n_interfaces != 1) { >> > > + return false; >> > > + } >> > > + >> > > + return ovn_is_evpn_tunnel_interface(port->interfaces[0]); >> > > +} >> > > + >> > > +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; >> > > +} >> > > + >> > > +static struct evpn_binding * >> > > +evpn_binding_add(struct hmap *evpn_bindings, >> > > + const struct evpn_remote_vtep *vtep, uint32_t >> > tunnel_key) >> > > +{ >> > > + struct evpn_binding *binding = xmalloc(sizeof *binding); >> > > + *binding = (struct evpn_binding) { >> > > + .flow_uuid = uuid_random(), >> > > + .remote_ip = vtep->ip, >> > > + .vni = vtep->vni, >> > > + .tunnel_key = tunnel_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); >> > > + 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..88d8c202c >> > > --- /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 tunnel_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 90ef6011a..996dcc021 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; >> > > +}; >> > >> > Nit: I'd add some empty lines between the fields here. I think it would >> > be a bit more readable. >> > >> >> >> I kinda prefer them as is, but if you feel like I should >> add empty lines. I can do it in v3. >> >> >> > >> > > + >> > > 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,152 @@ 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 (!ovn_is_evpn_tunnel_interface(iface)) { >> > > + continue; >> > > + } >> > > + >> > > + if (ovsrec_interface_is_new(iface) || >> > > + ovsrec_interface_is_deleted(iface) || >> > > + ovsrec_interface_is_updated(iface, >> > OVSREC_INTERFACE_COL_OFPORT)) { >> > nit: I would at least add a comment on why handling ovs interface option > change (e.g. handling setting "flow" on local_ip) > is not needed. I think that it is not needed as (among other potential > recomputes) ovs interface changes for vxlan > port w/o iface-id would result in runtime_data recompute (we might also > wonder whether runtime_data recompute > on vxlan changes is really needed, but this can be done in a > separate/later optimization patch). > Right, but this won't actually be needed in v3 because ovn-controller will create the tunnel so the interface option will be stable. > > > + 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 neighbor. >> */ >> > >> > Nit: maybe the more explicit "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. >> > > */ >> > > @@ -6257,6 +6413,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 >> > > @@ -6492,6 +6649,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, >> > > @@ -6511,9 +6684,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, >> > > @@ -6573,6 +6743,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); >> > > @@ -6592,6 +6764,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.c b/lib/ovn-util.c >> > > index c12de4da9..e8a32c96a 100644 >> > > --- a/lib/ovn-util.c >> > > +++ b/lib/ovn-util.c >> > > @@ -1490,3 +1490,19 @@ ovn_is_valid_vni(int64_t vni) >> > > { >> > > return vni >= 0 && (vni <= (1 << 24) - 1); >> > > } >> > > + >> > > +bool >> > > +ovn_is_evpn_tunnel_interface(const struct ovsrec_interface *iface) >> > > +{ >> > > + if (strcmp(iface->type, "vxlan")) { >> > > + return false; >> > > + } >> > > + >> > > + if (strcmp(smap_get_def(&iface->options, "local_ip", ""), >> "flow") || >> > > + strcmp(smap_get_def(&iface->options, "remote_ip", ""), >> "flow") >> > || >> > > + strcmp(smap_get_def(&iface->options, "key", ""), "flow")) { >> > > + return false; >> > > + } >> > > + >> > > + return true; >> > > +} >> > > diff --git a/lib/ovn-util.h b/lib/ovn-util.h >> > > index f31eba344..1bdd98675 100644 >> > > --- a/lib/ovn-util.h >> > > +++ b/lib/ovn-util.h >> > > @@ -49,6 +49,7 @@ struct svec; >> > > struct uuid; >> > > struct unixctl_conn; >> > > struct rconn; >> > > +struct ovsrec_interface; >> > > >> > > struct ipv4_netaddr { >> > > ovs_be32 addr; /* 192.168.10.123 */ >> > > @@ -167,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); >> > > @@ -501,6 +505,7 @@ bool find_prefix_in_set(const struct in6_addr >> > *prefix, unsigned int plen, >> > > void ovn_debug_commands_register(void); >> > > >> > > bool ovn_is_valid_vni(int64_t vni); >> > > +bool ovn_is_evpn_tunnel_interface(const struct ovsrec_interface >> *iface); >> > > >> > > const struct sbrec_port_binding *lport_lookup_by_name( >> > > struct ovsdb_idl_index *sbrec_port_binding_by_name, >> > >> > Regards, >> > Dumitru >> > >> > >> Everything else should be addressed in v2. >> >> Thanks, >> Ales >> _______________________________________________ >> dev mailing list >> d...@openvswitch.org >> https://mail.openvswitch.org/mailman/listinfo/ovs-dev > > Thanks, Ales _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev