> Move functionality for multicast and IGMP into new module. There > isn't any functional change just functions being moved around. > This is preparation for multicast and IGMP I-P node. > > Co-authored-by: Jacob Tanenbaum <[email protected]> > Signed-off-by: Jacob Tanenbaum <[email protected]> > Signed-off-by: Ales Musil <[email protected]>
Acked-by: Lorenzo Bianconi <[email protected]> > --- > northd/automake.mk | 2 + > northd/en-multicast.c | 662 +++++++++++++++++++++++++++++++++++++++++ > northd/en-multicast.h | 92 ++++++ > northd/northd.c | 672 +----------------------------------------- > northd/northd.h | 24 ++ > 5 files changed, 794 insertions(+), 658 deletions(-) > create mode 100644 northd/en-multicast.c > create mode 100644 northd/en-multicast.h > > diff --git a/northd/automake.mk b/northd/automake.mk > index 6566ad299..951ed7e34 100644 > --- a/northd/automake.mk > +++ b/northd/automake.mk > @@ -16,6 +16,8 @@ northd_ovn_northd_SOURCES = \ > northd/en-lflow.h \ > northd/en-meters.c \ > northd/en-meters.h \ > + northd/en-multicast.c \ > + northd/en-multicast.h \ > northd/en-northd-output.c \ > northd/en-northd-output.h \ > northd/en-port-group.c \ > diff --git a/northd/en-multicast.c b/northd/en-multicast.c > new file mode 100644 > index 000000000..deb192a82 > --- /dev/null > +++ b/northd/en-multicast.c > @@ -0,0 +1,662 @@ > +/* > + * 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> > + > +/* OVS includes. */ > +#include "openvswitch/hmap.h" > +#include "openvswitch/vlog.h" > + > +/* OVN includes. */ > +#include "en-multicast.h" > +#include "lib/ip-mcast-index.h" > +#include "lib/mcast-group-index.h" > +#include "lib/ovn-l7.h" > +#include "lib/ovn-nb-idl.h" > +#include "lib/ovn-sb-idl.h" > +#include "lib/ovn-util.h" > +#include "northd.h" > + > +VLOG_DEFINE_THIS_MODULE(en_multicast); > + > +static const struct multicast_group mc_flood = > + { MC_FLOOD, OVN_MCAST_FLOOD_TUNNEL_KEY }; > + > +static const struct multicast_group mc_mrouter_flood = > + { MC_MROUTER_FLOOD, OVN_MCAST_MROUTER_FLOOD_TUNNEL_KEY }; > + > +static const struct multicast_group mc_static = > + { MC_STATIC, OVN_MCAST_STATIC_TUNNEL_KEY }; > + > +static const struct multicast_group mc_unknown = > + { MC_UNKNOWN, OVN_MCAST_UNKNOWN_TUNNEL_KEY }; > + > +static const struct multicast_group mc_flood_l2 = > + { MC_FLOOD_L2, OVN_MCAST_FLOOD_L2_TUNNEL_KEY }; > + > +static bool multicast_group_equal(const struct multicast_group *, > + const struct multicast_group *); > +static uint32_t ovn_multicast_hash(const struct ovn_datapath *, > + const struct multicast_group *); > +static struct ovn_multicast *ovn_multicast_find( > + struct hmap *mcgroups, struct ovn_datapath *, > + const struct multicast_group *); > +static void ovn_multicast_add_ports(struct hmap *mcgroups, > + struct ovn_datapath *, > + const struct multicast_group *, > + struct ovn_port **ports, size_t n_ports); > +static void ovn_multicast_add(struct hmap *mcgroups, > + const struct multicast_group *, > + struct ovn_port *); > +static void ovn_multicast_destroy(struct hmap *mcgroups, > + struct ovn_multicast *); > +static void ovn_multicast_update_sbrec(const struct ovn_multicast *, > + const struct sbrec_multicast_group *); > + > +static uint32_t ovn_igmp_group_hash(const struct ovn_datapath *, > + const struct in6_addr *); > +static struct ovn_igmp_group * ovn_igmp_group_find(struct hmap *igmp_groups, > + const struct ovn_datapath > *, > + const struct in6_addr *); > +static struct ovn_igmp_group *ovn_igmp_group_add( > + struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp, > + struct hmap *igmp_groups, struct ovn_datapath *, > + const struct in6_addr *, const char *address_s); > +static struct ovn_port **ovn_igmp_group_get_ports( > + const struct sbrec_igmp_group *, size_t *n_ports, > + const struct hmap *ls_ports); > +static void ovn_igmp_group_add_entry(struct ovn_igmp_group *, > + struct ovn_port **ports, size_t > n_ports); > +static void ovn_igmp_group_destroy_entry(struct ovn_igmp_group_entry *); > +static bool ovn_igmp_group_allocate_id(struct ovn_igmp_group *); > +static void ovn_igmp_mrouter_aggregate_ports(struct ovn_igmp_group *, > + struct hmap *mcast_groups); > +static void ovn_igmp_group_aggregate_ports(struct ovn_igmp_group *, > + struct hmap *mcast_groups); > +static void ovn_igmp_group_destroy(struct hmap *igmp_groups, > + struct ovn_igmp_group *); > + > +void > +build_mcast_groups(const struct sbrec_igmp_group_table > *sbrec_igmp_group_table, > + struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp, > + const struct hmap *ls_datapaths, > + const struct hmap *ls_ports, > + const struct hmap *lr_ports, > + struct hmap *mcast_groups, > + struct hmap *igmp_groups) > +{ > + struct ovn_datapath *od; > + struct ovn_port *op; > + > + hmap_init(mcast_groups); > + hmap_init(igmp_groups); > + > + HMAP_FOR_EACH (op, key_node, lr_ports) { > + if (lrport_is_enabled(op->nbrp)) { > + /* If this port is configured to always flood multicast traffic > + * add it to the MC_STATIC group. > + */ > + if (op->mcast_info.flood) { > + ovn_multicast_add(mcast_groups, &mc_static, op); > + op->od->mcast_info.rtr.flood_static = true; > + } > + } > + } > + > + HMAP_FOR_EACH (op, key_node, ls_ports) { > + if (lsp_is_enabled(op->nbsp)) { > + ovn_multicast_add(mcast_groups, &mc_flood, op); > + > + if (!lsp_is_router(op->nbsp)) { > + ovn_multicast_add(mcast_groups, &mc_flood_l2, op); > + } > + > + if (op->has_unknown) { > + ovn_multicast_add(mcast_groups, &mc_unknown, op); > + } > + > + /* If this port is connected to a multicast router then add it > + * to the MC_MROUTER_FLOOD group. > + */ > + if (op->od->mcast_info.sw.flood_relay && op->peer && > + op->peer->od && op->peer->od->mcast_info.rtr.relay) { > + ovn_multicast_add(mcast_groups, &mc_mrouter_flood, op); > + } > + > + /* If this port is configured to always flood multicast reports > + * add it to the MC_MROUTER_FLOOD group (all reports must be > + * flooded to statically configured or learned mrouters). > + */ > + if (op->mcast_info.flood_reports) { > + ovn_multicast_add(mcast_groups, &mc_mrouter_flood, op); > + op->od->mcast_info.sw.flood_reports = true; > + } > + > + /* If this port is configured to always flood multicast traffic > + * add it to the MC_STATIC group. > + */ > + if (op->mcast_info.flood) { > + ovn_multicast_add(mcast_groups, &mc_static, op); > + op->od->mcast_info.sw.flood_static = true; > + } > + } > + } > + > + const struct sbrec_igmp_group *sb_igmp; > + > + SBREC_IGMP_GROUP_TABLE_FOR_EACH_SAFE (sb_igmp, sbrec_igmp_group_table) { > + /* If this is a stale group (e.g., controller had crashed, > + * purge it). > + */ > + if (!sb_igmp->chassis || !sb_igmp->datapath) { > + sbrec_igmp_group_delete(sb_igmp); > + continue; > + } > + > + /* If the datapath value is stale, purge the group. */ > + od = ovn_datapath_from_sbrec(ls_datapaths, NULL, > + sb_igmp->datapath); > + > + if (!od || ovn_datapath_is_stale(od)) { > + sbrec_igmp_group_delete(sb_igmp); > + continue; > + } > + > + struct in6_addr group_address; > + if (!strcmp(sb_igmp->address, OVN_IGMP_GROUP_MROUTERS)) { > + /* Use all-zeros IP to denote a group corresponding to mrouters. > */ > + memset(&group_address, 0, sizeof group_address); > + } else if (!ip46_parse(sb_igmp->address, &group_address)) { > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); > + VLOG_WARN_RL(&rl, "invalid IGMP group address: %s", > + sb_igmp->address); > + continue; > + } > + > + /* Extract the IGMP group ports from the SB entry. */ > + size_t n_igmp_ports; > + struct ovn_port **igmp_ports = > + ovn_igmp_group_get_ports(sb_igmp, &n_igmp_ports, ls_ports); > + > + /* It can be that all ports in the IGMP group record already have > + * mcast_flood=true and then we can skip the group completely. > + */ > + if (!igmp_ports) { > + continue; > + } > + > + /* Add the IGMP group entry. Will also try to allocate an ID for it > + * if the multicast group already exists. > + */ > + struct ovn_igmp_group *igmp_group = > + ovn_igmp_group_add(sbrec_mcast_group_by_name_dp, igmp_groups, od, > + &group_address, sb_igmp->address); > + > + /* Add the extracted ports to the IGMP group. */ > + ovn_igmp_group_add_entry(igmp_group, igmp_ports, n_igmp_ports); > + } > + > + /* Build IGMP groups for multicast routers with relay enabled. The router > + * IGMP groups are based on the groups learnt by their multicast enabled > + * peers. > + */ > + HMAP_FOR_EACH (od, key_node, ls_datapaths) { > + > + if (ovs_list_is_empty(&od->mcast_info.groups)) { > + continue; > + } > + > + for (size_t i = 0; i < od->n_router_ports; i++) { > + struct ovn_port *router_port = od->router_ports[i]->peer; > + > + /* If the router the port connects to doesn't have multicast > + * relay enabled or if it was already configured to flood > + * multicast traffic then skip it. > + */ > + if (!router_port || !router_port->od || > + !router_port->od->mcast_info.rtr.relay || > + router_port->mcast_info.flood) { > + continue; > + } > + > + struct ovn_igmp_group *igmp_group; > + LIST_FOR_EACH (igmp_group, list_node, &od->mcast_info.groups) { > + struct in6_addr *address = &igmp_group->address; > + > + /* Skip mrouter entries. */ > + if (!strcmp(igmp_group->mcgroup.name, > + OVN_IGMP_GROUP_MROUTERS)) { > + continue; > + } > + > + /* For IPv6 only relay routable multicast groups > + * (RFC 4291 2.7). > + */ > + if (!IN6_IS_ADDR_V4MAPPED(address) && > + !ipv6_addr_is_routable_multicast(address)) { > + continue; > + } > + > + struct ovn_igmp_group *igmp_group_rtr = > + ovn_igmp_group_add(sbrec_mcast_group_by_name_dp, > + igmp_groups, router_port->od, > + address, igmp_group->mcgroup.name); > + struct ovn_port **router_igmp_ports = > + xmalloc(sizeof *router_igmp_ports); > + /* Store the chassis redirect port otherwise traffic will > not > + * be tunneled properly. > + */ > + router_igmp_ports[0] = router_port->cr_port > + ? router_port->cr_port > + : router_port; > + ovn_igmp_group_add_entry(igmp_group_rtr, router_igmp_ports, > 1); > + } > + } > + } > + > + /* Walk the aggregated IGMP groups and allocate IDs for new entries. > + * Then store the ports in the associated multicast group. > + * Mrouter entries are also stored as IGMP groups, deal with those > + * explicitly. > + */ > + struct ovn_igmp_group *igmp_group; > + HMAP_FOR_EACH_SAFE (igmp_group, hmap_node, igmp_groups) { > + > + /* If this is a mrouter entry just aggregate the mrouter ports > + * into the MC_MROUTER mcast_group and destroy the igmp_group; > + * no more processing needed. */ > + if (!strcmp(igmp_group->mcgroup.name, OVN_IGMP_GROUP_MROUTERS)) { > + ovn_igmp_mrouter_aggregate_ports(igmp_group, mcast_groups); > + ovn_igmp_group_destroy(igmp_groups, igmp_group); > + continue; > + } > + > + if (!ovn_igmp_group_allocate_id(igmp_group)) { > + /* If we ran out of keys just destroy the entry. */ > + ovn_igmp_group_destroy(igmp_groups, igmp_group); > + continue; > + } > + > + /* Aggregate the ports from all entries corresponding to this > + * group. > + */ > + ovn_igmp_group_aggregate_ports(igmp_group, mcast_groups); > + } > +} > + > +void > +sync_multicast_groups_to_sb( > + struct ovsdb_idl_txn *ovnsb_txn, > + const struct sbrec_multicast_group_table *sbrec_multicast_group_table, > + const struct hmap * ls_datapaths, const struct hmap *lr_datapaths, > + struct hmap *mcast_groups) > +{ > + /* Push changes to the Multicast_Group table to database. */ > + const struct sbrec_multicast_group *sbmc; > + SBREC_MULTICAST_GROUP_TABLE_FOR_EACH_SAFE ( > + sbmc, sbrec_multicast_group_table) { > + struct ovn_datapath *od = ovn_datapath_from_sbrec(ls_datapaths, > + lr_datapaths, > + sbmc->datapath); > + > + if (!od || ovn_datapath_is_stale(od)) { > + sbrec_multicast_group_delete(sbmc); > + continue; > + } > + > + struct multicast_group group = { .name = sbmc->name, > + .key = sbmc->tunnel_key }; > + struct ovn_multicast *mc = ovn_multicast_find(mcast_groups, > + od, &group); > + if (mc) { > + ovn_multicast_update_sbrec(mc, sbmc); > + ovn_multicast_destroy(mcast_groups, mc); > + } else { > + sbrec_multicast_group_delete(sbmc); > + } > + } > + struct ovn_multicast *mc; > + HMAP_FOR_EACH_SAFE (mc, hmap_node, mcast_groups) { > + if (!mc->datapath) { > + ovn_multicast_destroy(mcast_groups, mc); > + continue; > + } > + sbmc = create_sb_multicast_group(ovnsb_txn, mc->datapath->sb, > + mc->group->name, mc->group->key); > + ovn_multicast_update_sbrec(mc, sbmc); > + ovn_multicast_destroy(mcast_groups, mc); > + } > + > + hmap_destroy(mcast_groups); > +} > + > +void > +ovn_igmp_groups_destroy(struct hmap *igmp_groups) > +{ > + struct ovn_igmp_group *igmp_group; > + HMAP_FOR_EACH_SAFE (igmp_group, hmap_node, igmp_groups) { > + ovn_igmp_group_destroy(igmp_groups, igmp_group); > + } > + hmap_destroy(igmp_groups); > +} > + > +struct sbrec_multicast_group * > +create_sb_multicast_group(struct ovsdb_idl_txn *ovnsb_txn, > + const struct sbrec_datapath_binding *dp, > + const char *name, > + int64_t tunnel_key) > +{ > + struct sbrec_multicast_group *sbmc = > + sbrec_multicast_group_insert(ovnsb_txn); > + sbrec_multicast_group_set_datapath(sbmc, dp); > + sbrec_multicast_group_set_name(sbmc, name); > + sbrec_multicast_group_set_tunnel_key(sbmc, tunnel_key); > + return sbmc; > +} > + > + > +static bool > +multicast_group_equal(const struct multicast_group *a, > + const struct multicast_group *b) > +{ > + return !strcmp(a->name, b->name) && a->key == b->key; > +} > + > + > +static uint32_t > +ovn_multicast_hash(const struct ovn_datapath *datapath, > + const struct multicast_group *group) > +{ > + return hash_pointer(datapath, group->key); > +} > + > +static struct ovn_multicast * > +ovn_multicast_find(struct hmap *mcgroups, struct ovn_datapath *datapath, > + const struct multicast_group *group) > +{ > + struct ovn_multicast *mc; > + > + HMAP_FOR_EACH_WITH_HASH (mc, hmap_node, > + ovn_multicast_hash(datapath, group), mcgroups) { > + if (mc->datapath == datapath > + && multicast_group_equal(mc->group, group)) { > + return mc; > + } > + } > + return NULL; > +} > + > +static void > +ovn_multicast_add_ports(struct hmap *mcgroups, struct ovn_datapath *od, > + const struct multicast_group *group, > + struct ovn_port **ports, size_t n_ports) > +{ > + struct ovn_multicast *mc = ovn_multicast_find(mcgroups, od, group); > + if (!mc) { > + mc = xmalloc(sizeof *mc); > + hmap_insert(mcgroups, &mc->hmap_node, ovn_multicast_hash(od, group)); > + mc->datapath = od; > + mc->group = group; > + mc->n_ports = 0; > + mc->allocated_ports = 4; > + mc->ports = xmalloc(mc->allocated_ports * sizeof *mc->ports); > + } > + > + size_t n_ports_total = mc->n_ports + n_ports; > + > + if (n_ports_total > 2 * mc->allocated_ports) { > + mc->allocated_ports = n_ports_total; > + mc->ports = xrealloc(mc->ports, > + mc->allocated_ports * sizeof *mc->ports); > + } else if (n_ports_total > mc->allocated_ports) { > + mc->ports = x2nrealloc(mc->ports, &mc->allocated_ports, > + sizeof *mc->ports); > + } > + > + memcpy(&mc->ports[mc->n_ports], &ports[0], n_ports * sizeof *ports); > + mc->n_ports += n_ports; > +} > + > +static void > +ovn_multicast_add(struct hmap *mcgroups, const struct multicast_group *group, > + struct ovn_port *port) > +{ > + /* Store the chassis redirect port otherwise traffic will not be tunneled > + * properly. > + */ > + if (port->cr_port) { > + port = port->cr_port; > + } > + ovn_multicast_add_ports(mcgroups, port->od, group, &port, 1); > +} > + > +static void > +ovn_multicast_destroy(struct hmap *mcgroups, struct ovn_multicast *mc) > +{ > + if (mc) { > + hmap_remove(mcgroups, &mc->hmap_node); > + free(mc->ports); > + free(mc); > + } > +} > + > +static void > +ovn_multicast_update_sbrec(const struct ovn_multicast *mc, > + const struct sbrec_multicast_group *sb) > +{ > + struct sbrec_port_binding **ports = xmalloc(mc->n_ports * sizeof *ports); > + for (size_t i = 0; i < mc->n_ports; i++) { > + ports[i] = CONST_CAST(struct sbrec_port_binding *, mc->ports[i]->sb); > + } > + sbrec_multicast_group_set_ports(sb, ports, mc->n_ports); > + free(ports); > +} > + > +static uint32_t > +ovn_igmp_group_hash(const struct ovn_datapath *datapath, > + const struct in6_addr *address) > +{ > + return hash_pointer(datapath, hash_bytes(address, sizeof *address, 0)); > +} > + > +static struct ovn_igmp_group * > +ovn_igmp_group_find(struct hmap *igmp_groups, > + const struct ovn_datapath *datapath, > + const struct in6_addr *address) > +{ > + struct ovn_igmp_group *group; > + > + HMAP_FOR_EACH_WITH_HASH (group, hmap_node, > + ovn_igmp_group_hash(datapath, address), > + igmp_groups) { > + if (group->datapath == datapath && > + ipv6_addr_equals(&group->address, address)) { > + return group; > + } > + } > + return NULL; > +} > + > +static struct ovn_igmp_group * > +ovn_igmp_group_add(struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp, > + struct hmap *igmp_groups, > + struct ovn_datapath *datapath, > + const struct in6_addr *address, > + const char *address_s) > +{ > + struct ovn_igmp_group *igmp_group = > + ovn_igmp_group_find(igmp_groups, datapath, address); > + > + if (!igmp_group) { > + igmp_group = xmalloc(sizeof *igmp_group); > + > + const struct sbrec_multicast_group *mcgroup = > + mcast_group_lookup(sbrec_mcast_group_by_name_dp, > + address_s, > + datapath->sb); > + > + igmp_group->datapath = datapath; > + igmp_group->address = *address; > + if (mcgroup) { > + igmp_group->mcgroup.key = mcgroup->tunnel_key; > + ovn_add_tnlid(&datapath->mcast_info.group_tnlids, > + mcgroup->tunnel_key); > + } else { > + igmp_group->mcgroup.key = 0; > + } > + igmp_group->mcgroup.name = address_s; > + ovs_list_init(&igmp_group->entries); > + > + hmap_insert(igmp_groups, &igmp_group->hmap_node, > + ovn_igmp_group_hash(datapath, address)); > + ovs_list_push_back(&datapath->mcast_info.groups, > + &igmp_group->list_node); > + } > + > + return igmp_group; > +} > + > +static struct ovn_port ** > +ovn_igmp_group_get_ports(const struct sbrec_igmp_group *sb_igmp_group, > + size_t *n_ports, const struct hmap *ls_ports) > +{ > + struct ovn_port **ports = NULL; > + > + *n_ports = 0; > + for (size_t i = 0; i < sb_igmp_group->n_ports; i++) { > + struct ovn_port *port = > + ovn_port_find(ls_ports, sb_igmp_group->ports[i]->logical_port); > + > + if (!port || !port->nbsp) { > + continue; > + } > + > + /* If this is already a flood port skip it for the group. */ > + if (port->mcast_info.flood) { > + continue; > + } > + > + /* If this is already a port of a router on which relay is enabled, > + * skip it for the group. Traffic is flooded there anyway. > + */ > + if (port->peer && port->peer->od && > + port->peer->od->mcast_info.rtr.relay) { > + continue; > + } > + > + if (ports == NULL) { > + ports = xmalloc(sb_igmp_group->n_ports * sizeof *ports); > + } > + > + ports[(*n_ports)] = port; > + (*n_ports)++; > + } > + > + return ports; > +} > + > +static void > +ovn_igmp_group_add_entry(struct ovn_igmp_group *igmp_group, > + struct ovn_port **ports, size_t n_ports) > +{ > + struct ovn_igmp_group_entry *entry = xmalloc(sizeof *entry); > + > + entry->ports = ports; > + entry->n_ports = n_ports; > + ovs_list_push_back(&igmp_group->entries, &entry->list_node); > +} > + > +static void > +ovn_igmp_group_destroy_entry(struct ovn_igmp_group_entry *entry) > +{ > + free(entry->ports); > +} > + > +static bool > +ovn_igmp_group_allocate_id(struct ovn_igmp_group *igmp_group) > +{ > + if (igmp_group->mcgroup.key == 0) { > + struct hmap *tnlids = &igmp_group->datapath->mcast_info.group_tnlids; > + uint32_t tnlid_hint = > + igmp_group->datapath->mcast_info.group_tnlid_hint; > + igmp_group->mcgroup.key = ovn_allocate_tnlid(tnlids, "multicast > group", > + OVN_MIN_IP_MULTICAST, > + OVN_MAX_IP_MULTICAST, > + &tnlid_hint); > + } > + > + if (igmp_group->mcgroup.key == 0) { > + return false; > + } > + > + return true; > +} > + > +static void > +ovn_igmp_mrouter_aggregate_ports(struct ovn_igmp_group *igmp_group, > + struct hmap *mcast_groups) > +{ > + struct ovn_igmp_group_entry *entry; > + > + LIST_FOR_EACH_POP (entry, list_node, &igmp_group->entries) { > + ovn_multicast_add_ports(mcast_groups, igmp_group->datapath, > + &mc_mrouter_flood, entry->ports, > + entry->n_ports); > + > + ovn_igmp_group_destroy_entry(entry); > + free(entry); > + } > +} > + > +static void > +ovn_igmp_group_aggregate_ports(struct ovn_igmp_group *igmp_group, > + struct hmap *mcast_groups) > +{ > + struct ovn_igmp_group_entry *entry; > + > + LIST_FOR_EACH_POP (entry, list_node, &igmp_group->entries) { > + ovn_multicast_add_ports(mcast_groups, igmp_group->datapath, > + &igmp_group->mcgroup, entry->ports, > + entry->n_ports); > + > + ovn_igmp_group_destroy_entry(entry); > + free(entry); > + } > + > + if (igmp_group->datapath->n_localnet_ports) { > + ovn_multicast_add_ports(mcast_groups, igmp_group->datapath, > + &igmp_group->mcgroup, > + igmp_group->datapath->localnet_ports, > + igmp_group->datapath->n_localnet_ports); > + } > +} > + > +static void > +ovn_igmp_group_destroy(struct hmap *igmp_groups, > + struct ovn_igmp_group *igmp_group) > +{ > + if (igmp_group) { > + struct ovn_igmp_group_entry *entry; > + > + LIST_FOR_EACH_POP (entry, list_node, &igmp_group->entries) { > + ovn_igmp_group_destroy_entry(entry); > + free(entry); > + } > + hmap_remove(igmp_groups, &igmp_group->hmap_node); > + ovs_list_remove(&igmp_group->list_node); > + free(igmp_group); > + } > +} > diff --git a/northd/en-multicast.h b/northd/en-multicast.h > new file mode 100644 > index 000000000..5fa4d8976 > --- /dev/null > +++ b/northd/en-multicast.h > @@ -0,0 +1,92 @@ > +/* > + * 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 OVN_EN_MULTICAST_H > +#define OVN_EN_MULTICAST_H > + > +#include <stdint.h> > + > +/* OVS includes. */ > +#include "openvswitch/hmap.h" > + > +/* OVN includes. */ > +#include "lib/ovn-sb-idl.h" > +#include "northd.h" > + > +#define MC_FLOOD "_MC_flood" > +#define MC_MROUTER_FLOOD "_MC_mrouter_flood" > +#define MC_STATIC "_MC_static" > +#define MC_UNKNOWN "_MC_unknown" > +#define MC_FLOOD_L2 "_MC_flood_l2" > + > +struct multicast_group { > + const char *name; > + uint16_t key; /* OVN_MIN_MULTICAST...OVN_MAX_MULTICAST. */ > +}; > + > +/* Multicast group entry. */ > +struct ovn_multicast { > + struct hmap_node hmap_node; /* Index on 'datapath' and 'key'. */ > + struct ovn_datapath *datapath; > + const struct multicast_group *group; > + > + struct ovn_port **ports; > + size_t n_ports, allocated_ports; > +}; > + > +/* > + * IGMP group entry (1:1 mapping to SB database). > + */ > +struct ovn_igmp_group_entry { > + struct ovs_list list_node; /* Linkage in the list of entries. */ > + size_t n_ports; > + struct ovn_port **ports; > +}; > + > +/* > + * IGMP group entry (aggregate of all entries from the SB database > + * corresponding to the multicast group). > + */ > +struct ovn_igmp_group { > + struct hmap_node hmap_node; /* Index on 'datapath' and 'address'. */ > + struct ovs_list list_node; /* Linkage in the per-dp igmp group list. */ > + > + struct ovn_datapath *datapath; > + struct in6_addr address; /* Multicast IPv6-mapped-IPv4 or IPv4 address. > */ > + struct multicast_group mcgroup; > + > + struct ovs_list entries; /* List of SB entries for this group. */ > +}; > + > +void build_mcast_groups( > + const struct sbrec_igmp_group_table *sbrec_igmp_group_table, > + struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp, > + const struct hmap *ls_datapaths, > + const struct hmap *ls_ports, > + const struct hmap *lr_ports, > + struct hmap *mcast_groups, > + struct hmap *igmp_groups); > +void sync_multicast_groups_to_sb( > + struct ovsdb_idl_txn *ovnsb_txn, > + const struct sbrec_multicast_group_table *sbrec_multicast_group_table, > + const struct hmap * ls_datapaths, const struct hmap *lr_datapaths, > + struct hmap *mcast_groups); > +void ovn_igmp_groups_destroy(struct hmap *igmp_groups); > +struct sbrec_multicast_group *create_sb_multicast_group( > + struct ovsdb_idl_txn *ovnsb_txn, const struct sbrec_datapath_binding *, > + const char *name, int64_t tunnel_key); > + > +#endif /* OVN_EN_MULTICAST_H */ > diff --git a/northd/northd.c b/northd/northd.c > index 3cf5d0ad7..ddd05f7e4 100644 > --- a/northd/northd.c > +++ b/northd/northd.c > @@ -49,6 +49,7 @@ > #include "en-lr-nat.h" > #include "en-lr-stateful.h" > #include "en-ls-stateful.h" > +#include "en-multicast.h" > #include "en-sampling-app.h" > #include "lib/ovn-parallel-hmap.h" > #include "ovn/actions.h" > @@ -366,14 +367,6 @@ init_mcast_port_info(struct mcast_port_info *mcast_info, > } > } > > -static uint32_t > -ovn_mcast_group_allocate_key(struct mcast_info *mcast_info) > -{ > - return ovn_allocate_tnlid(&mcast_info->group_tnlids, "multicast group", > - OVN_MIN_IP_MULTICAST, OVN_MAX_IP_MULTICAST, > - &mcast_info->group_tnlid_hint); > -} > - > static bool > lb_has_vip(const struct nbrec_load_balancer *lb) > { > @@ -1205,8 +1198,6 @@ ovn_port_set_nb(struct ovn_port *op, > init_mcast_port_info(&op->mcast_info, op->nbsp, op->nbrp); > } > > -static bool lsp_is_router(const struct nbrec_logical_switch_port *nbsp); > - > static struct ovn_port * > ovn_port_create(struct hmap *ports, const char *key, > const struct nbrec_logical_switch_port *nbsp, > @@ -1308,7 +1299,7 @@ ovn_port_find__(const struct hmap *ports, const char > *name, > return matched_op; > } > > -static struct ovn_port * > +struct ovn_port * > ovn_port_find(const struct hmap *ports, const char *name) > { > return ovn_port_find__(ports, name, false); > @@ -1334,14 +1325,6 @@ ovn_port_find_bound(const struct hmap *ports, const > char *name) > return ovn_port_find__(ports, name, true); > } > > -/* Returns true if the logical switch port 'enabled' column is empty or > - * set to true. Otherwise, returns false. */ > -static bool > -lsp_is_enabled(const struct nbrec_logical_switch_port *lsp) > -{ > - return !lsp->n_enabled || *lsp->enabled; > -} > - > /* Returns true only if the logical switch port 'up' column is set to true. > * Otherwise, if the column is not set or set to false, returns false. */ > static bool > @@ -1356,12 +1339,6 @@ lsp_is_external(const struct nbrec_logical_switch_port > *nbsp) > return !strcmp(nbsp->type, "external"); > } > > -static bool > -lsp_is_router(const struct nbrec_logical_switch_port *nbsp) > -{ > - return !strcmp(nbsp->type, "router"); > -} > - > static bool > lsp_is_remote(const struct nbrec_logical_switch_port *nbsp) > { > @@ -1430,12 +1407,6 @@ lsp_is_type_changed(const struct sbrec_port_binding > *sb, > return true; > } > > -static bool > -lrport_is_enabled(const struct nbrec_logical_router_port *lrport) > -{ > - return !lrport->enabled || *lrport->enabled; > -} > - > static bool > lsp_force_fdb_lookup(const struct ovn_port *op) > { > @@ -5328,352 +5299,6 @@ northd_handle_lb_data_changes(struct tracked_lb_data > *trk_lb_data, > return true; > } > > -struct multicast_group { > - const char *name; > - uint16_t key; /* OVN_MIN_MULTICAST...OVN_MAX_MULTICAST. */ > -}; > - > -#define MC_FLOOD "_MC_flood" > -static const struct multicast_group mc_flood = > - { MC_FLOOD, OVN_MCAST_FLOOD_TUNNEL_KEY }; > - > -#define MC_MROUTER_FLOOD "_MC_mrouter_flood" > -static const struct multicast_group mc_mrouter_flood = > - { MC_MROUTER_FLOOD, OVN_MCAST_MROUTER_FLOOD_TUNNEL_KEY }; > - > -#define MC_STATIC "_MC_static" > -static const struct multicast_group mc_static = > - { MC_STATIC, OVN_MCAST_STATIC_TUNNEL_KEY }; > - > -#define MC_UNKNOWN "_MC_unknown" > -static const struct multicast_group mc_unknown = > - { MC_UNKNOWN, OVN_MCAST_UNKNOWN_TUNNEL_KEY }; > - > -#define MC_FLOOD_L2 "_MC_flood_l2" > -static const struct multicast_group mc_flood_l2 = > - { MC_FLOOD_L2, OVN_MCAST_FLOOD_L2_TUNNEL_KEY }; > - > -static bool > -multicast_group_equal(const struct multicast_group *a, > - const struct multicast_group *b) > -{ > - return !strcmp(a->name, b->name) && a->key == b->key; > -} > - > -/* Multicast group entry. */ > -struct ovn_multicast { > - struct hmap_node hmap_node; /* Index on 'datapath' and 'key'. */ > - struct ovn_datapath *datapath; > - const struct multicast_group *group; > - > - struct ovn_port **ports; > - size_t n_ports, allocated_ports; > -}; > - > -static uint32_t > -ovn_multicast_hash(const struct ovn_datapath *datapath, > - const struct multicast_group *group) > -{ > - return hash_pointer(datapath, group->key); > -} > - > -static struct ovn_multicast * > -ovn_multicast_find(struct hmap *mcgroups, struct ovn_datapath *datapath, > - const struct multicast_group *group) > -{ > - struct ovn_multicast *mc; > - > - HMAP_FOR_EACH_WITH_HASH (mc, hmap_node, > - ovn_multicast_hash(datapath, group), mcgroups) { > - if (mc->datapath == datapath > - && multicast_group_equal(mc->group, group)) { > - return mc; > - } > - } > - return NULL; > -} > - > -static void > -ovn_multicast_add_ports(struct hmap *mcgroups, struct ovn_datapath *od, > - const struct multicast_group *group, > - struct ovn_port **ports, size_t n_ports) > -{ > - struct ovn_multicast *mc = ovn_multicast_find(mcgroups, od, group); > - if (!mc) { > - mc = xmalloc(sizeof *mc); > - hmap_insert(mcgroups, &mc->hmap_node, ovn_multicast_hash(od, group)); > - mc->datapath = od; > - mc->group = group; > - mc->n_ports = 0; > - mc->allocated_ports = 4; > - mc->ports = xmalloc(mc->allocated_ports * sizeof *mc->ports); > - } > - > - size_t n_ports_total = mc->n_ports + n_ports; > - > - if (n_ports_total > 2 * mc->allocated_ports) { > - mc->allocated_ports = n_ports_total; > - mc->ports = xrealloc(mc->ports, > - mc->allocated_ports * sizeof *mc->ports); > - } else if (n_ports_total > mc->allocated_ports) { > - mc->ports = x2nrealloc(mc->ports, &mc->allocated_ports, > - sizeof *mc->ports); > - } > - > - memcpy(&mc->ports[mc->n_ports], &ports[0], n_ports * sizeof *ports); > - mc->n_ports += n_ports; > -} > - > -static void > -ovn_multicast_add(struct hmap *mcgroups, const struct multicast_group *group, > - struct ovn_port *port) > -{ > - /* Store the chassis redirect port otherwise traffic will not be tunneled > - * properly. > - */ > - if (port->cr_port) { > - port = port->cr_port; > - } > - ovn_multicast_add_ports(mcgroups, port->od, group, &port, 1); > -} > - > -static void > -ovn_multicast_destroy(struct hmap *mcgroups, struct ovn_multicast *mc) > -{ > - if (mc) { > - hmap_remove(mcgroups, &mc->hmap_node); > - free(mc->ports); > - free(mc); > - } > -} > - > -static void > -ovn_multicast_update_sbrec(const struct ovn_multicast *mc, > - const struct sbrec_multicast_group *sb) > -{ > - struct sbrec_port_binding **ports = xmalloc(mc->n_ports * sizeof *ports); > - for (size_t i = 0; i < mc->n_ports; i++) { > - ports[i] = CONST_CAST(struct sbrec_port_binding *, mc->ports[i]->sb); > - } > - sbrec_multicast_group_set_ports(sb, ports, mc->n_ports); > - free(ports); > -} > - > -/* > - * IGMP group entry (1:1 mapping to SB database). > - */ > -struct ovn_igmp_group_entry { > - struct ovs_list list_node; /* Linkage in the list of entries. */ > - size_t n_ports; > - struct ovn_port **ports; > -}; > - > -/* > - * IGMP group entry (aggregate of all entries from the SB database > - * corresponding to the multicast group). > - */ > -struct ovn_igmp_group { > - struct hmap_node hmap_node; /* Index on 'datapath' and 'address'. */ > - struct ovs_list list_node; /* Linkage in the per-dp igmp group list. */ > - > - struct ovn_datapath *datapath; > - struct in6_addr address; /* Multicast IPv6-mapped-IPv4 or IPv4 address. > */ > - struct multicast_group mcgroup; > - > - struct ovs_list entries; /* List of SB entries for this group. */ > -}; > - > -static uint32_t > -ovn_igmp_group_hash(const struct ovn_datapath *datapath, > - const struct in6_addr *address) > -{ > - return hash_pointer(datapath, hash_bytes(address, sizeof *address, 0)); > -} > - > -static struct ovn_igmp_group * > -ovn_igmp_group_find(struct hmap *igmp_groups, > - const struct ovn_datapath *datapath, > - const struct in6_addr *address) > -{ > - struct ovn_igmp_group *group; > - > - HMAP_FOR_EACH_WITH_HASH (group, hmap_node, > - ovn_igmp_group_hash(datapath, address), > - igmp_groups) { > - if (group->datapath == datapath && > - ipv6_addr_equals(&group->address, address)) { > - return group; > - } > - } > - return NULL; > -} > - > -static struct ovn_igmp_group * > -ovn_igmp_group_add(struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp, > - struct hmap *igmp_groups, > - struct ovn_datapath *datapath, > - const struct in6_addr *address, > - const char *address_s) > -{ > - struct ovn_igmp_group *igmp_group = > - ovn_igmp_group_find(igmp_groups, datapath, address); > - > - if (!igmp_group) { > - igmp_group = xmalloc(sizeof *igmp_group); > - > - const struct sbrec_multicast_group *mcgroup = > - mcast_group_lookup(sbrec_mcast_group_by_name_dp, > - address_s, > - datapath->sb); > - > - igmp_group->datapath = datapath; > - igmp_group->address = *address; > - if (mcgroup) { > - igmp_group->mcgroup.key = mcgroup->tunnel_key; > - ovn_add_tnlid(&datapath->mcast_info.group_tnlids, > - mcgroup->tunnel_key); > - } else { > - igmp_group->mcgroup.key = 0; > - } > - igmp_group->mcgroup.name = address_s; > - ovs_list_init(&igmp_group->entries); > - > - hmap_insert(igmp_groups, &igmp_group->hmap_node, > - ovn_igmp_group_hash(datapath, address)); > - ovs_list_push_back(&datapath->mcast_info.groups, > - &igmp_group->list_node); > - } > - > - return igmp_group; > -} > - > -static struct ovn_port ** > -ovn_igmp_group_get_ports(const struct sbrec_igmp_group *sb_igmp_group, > - size_t *n_ports, const struct hmap *ls_ports) > -{ > - struct ovn_port **ports = NULL; > - > - *n_ports = 0; > - for (size_t i = 0; i < sb_igmp_group->n_ports; i++) { > - struct ovn_port *port = > - ovn_port_find(ls_ports, sb_igmp_group->ports[i]->logical_port); > - > - if (!port || !port->nbsp) { > - continue; > - } > - > - /* If this is already a flood port skip it for the group. */ > - if (port->mcast_info.flood) { > - continue; > - } > - > - /* If this is already a port of a router on which relay is enabled, > - * skip it for the group. Traffic is flooded there anyway. > - */ > - if (port->peer && port->peer->od && > - port->peer->od->mcast_info.rtr.relay) { > - continue; > - } > - > - if (ports == NULL) { > - ports = xmalloc(sb_igmp_group->n_ports * sizeof *ports); > - } > - > - ports[(*n_ports)] = port; > - (*n_ports)++; > - } > - > - return ports; > -} > - > -static void > -ovn_igmp_group_add_entry(struct ovn_igmp_group *igmp_group, > - struct ovn_port **ports, size_t n_ports) > -{ > - struct ovn_igmp_group_entry *entry = xmalloc(sizeof *entry); > - > - entry->ports = ports; > - entry->n_ports = n_ports; > - ovs_list_push_back(&igmp_group->entries, &entry->list_node); > -} > - > -static void > -ovn_igmp_group_destroy_entry(struct ovn_igmp_group_entry *entry) > -{ > - free(entry->ports); > -} > - > -static bool > -ovn_igmp_group_allocate_id(struct ovn_igmp_group *igmp_group) > -{ > - if (igmp_group->mcgroup.key == 0) { > - struct mcast_info *mcast_info = &igmp_group->datapath->mcast_info; > - igmp_group->mcgroup.key = ovn_mcast_group_allocate_key(mcast_info); > - } > - > - if (igmp_group->mcgroup.key == 0) { > - return false; > - } > - > - return true; > -} > - > -static void > -ovn_igmp_mrouter_aggregate_ports(struct ovn_igmp_group *igmp_group, > - struct hmap *mcast_groups) > -{ > - struct ovn_igmp_group_entry *entry; > - > - LIST_FOR_EACH_POP (entry, list_node, &igmp_group->entries) { > - ovn_multicast_add_ports(mcast_groups, igmp_group->datapath, > - &mc_mrouter_flood, entry->ports, > - entry->n_ports); > - > - ovn_igmp_group_destroy_entry(entry); > - free(entry); > - } > -} > - > -static void > -ovn_igmp_group_aggregate_ports(struct ovn_igmp_group *igmp_group, > - struct hmap *mcast_groups) > -{ > - struct ovn_igmp_group_entry *entry; > - > - LIST_FOR_EACH_POP (entry, list_node, &igmp_group->entries) { > - ovn_multicast_add_ports(mcast_groups, igmp_group->datapath, > - &igmp_group->mcgroup, entry->ports, > - entry->n_ports); > - > - ovn_igmp_group_destroy_entry(entry); > - free(entry); > - } > - > - if (igmp_group->datapath->n_localnet_ports) { > - ovn_multicast_add_ports(mcast_groups, igmp_group->datapath, > - &igmp_group->mcgroup, > - igmp_group->datapath->localnet_ports, > - igmp_group->datapath->n_localnet_ports); > - } > -} > - > -static void > -ovn_igmp_group_destroy(struct hmap *igmp_groups, > - struct ovn_igmp_group *igmp_group) > -{ > - if (igmp_group) { > - struct ovn_igmp_group_entry *entry; > - > - LIST_FOR_EACH_POP (entry, list_node, &igmp_group->entries) { > - ovn_igmp_group_destroy_entry(entry); > - free(entry); > - } > - hmap_remove(igmp_groups, &igmp_group->hmap_node); > - ovs_list_remove(&igmp_group->list_node); > - free(igmp_group); > - } > -} > - > /* Logical flow generation. > * > * This code generates the Logical_Flow table in the southbound database, as > a > @@ -17922,29 +17547,6 @@ void run_update_worker_pool(int n_threads) > } > } > > -static void > -build_mcast_groups(const struct sbrec_igmp_group_table > *sbrec_igmp_group_table, > - struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp, > - const struct ovn_datapaths *ls_datapaths, > - const struct hmap *ls_ports, > - const struct hmap *lr_ports, > - struct hmap *mcast_groups, > - struct hmap *igmp_groups); > - > -static struct sbrec_multicast_group * > -create_sb_multicast_group(struct ovsdb_idl_txn *ovnsb_txn, > - const struct sbrec_datapath_binding *dp, > - const char *name, > - int64_t tunnel_key) > -{ > - struct sbrec_multicast_group *sbmc = > - sbrec_multicast_group_insert(ovnsb_txn); > - sbrec_multicast_group_set_datapath(sbmc, dp); > - sbrec_multicast_group_set_name(sbmc, name); > - sbrec_multicast_group_set_tunnel_key(sbmc, tunnel_key); > - return sbmc; > -} > - > /* Updates the Logical_Flow and Multicast_Group tables in the OVN_SB > database, > * constructing their contents based on the OVN_NB database. */ > void build_lflows(struct ovsdb_idl_txn *ovnsb_txn, > @@ -17954,9 +17556,14 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn, > struct hmap mcast_groups; > struct hmap igmp_groups; > > + struct ovn_datapath *od; > + HMAP_FOR_EACH (od, key_node, &input_data->ls_datapaths->datapaths) { > + init_mcast_flow_count(od); > + } > + > build_mcast_groups(input_data->sbrec_igmp_group_table, > input_data->sbrec_mcast_group_by_name_dp, > - input_data->ls_datapaths, > + &input_data->ls_datapaths->datapaths, > input_data->ls_ports, input_data->lr_ports, > &mcast_groups, &igmp_groups); > > @@ -17997,51 +17604,12 @@ void build_lflows(struct ovsdb_idl_txn *ovnsb_txn, > > stopwatch_stop(LFLOWS_TO_SB_STOPWATCH_NAME, time_msec()); > > - /* Push changes to the Multicast_Group table to database. */ > - const struct sbrec_multicast_group *sbmc; > - SBREC_MULTICAST_GROUP_TABLE_FOR_EACH_SAFE ( > - sbmc, input_data->sbrec_multicast_group_table) { > - struct ovn_datapath *od = ovn_datapath_from_sbrec( > - &input_data->ls_datapaths->datapaths, > - &input_data->lr_datapaths->datapaths, > - sbmc->datapath); > - > - if (!od || ovn_datapath_is_stale(od)) { > - sbrec_multicast_group_delete(sbmc); > - continue; > - } > - > - struct multicast_group group = { .name = sbmc->name, > - .key = sbmc->tunnel_key }; > - struct ovn_multicast *mc = ovn_multicast_find(&mcast_groups, > - od, &group); > - if (mc) { > - ovn_multicast_update_sbrec(mc, sbmc); > - ovn_multicast_destroy(&mcast_groups, mc); > - } else { > - sbrec_multicast_group_delete(sbmc); > - } > - } > - struct ovn_multicast *mc; > - HMAP_FOR_EACH_SAFE (mc, hmap_node, &mcast_groups) { > - if (!mc->datapath) { > - ovn_multicast_destroy(&mcast_groups, mc); > - continue; > - } > - sbmc = create_sb_multicast_group(ovnsb_txn, mc->datapath->sb, > - mc->group->name, mc->group->key); > - ovn_multicast_update_sbrec(mc, sbmc); > - ovn_multicast_destroy(&mcast_groups, mc); > - } > - > - struct ovn_igmp_group *igmp_group; > - > - HMAP_FOR_EACH_SAFE (igmp_group, hmap_node, &igmp_groups) { > - ovn_igmp_group_destroy(&igmp_groups, igmp_group); > - } > - > - hmap_destroy(&igmp_groups); > - hmap_destroy(&mcast_groups); > + sync_multicast_groups_to_sb(ovnsb_txn, > + input_data->sbrec_multicast_group_table, > + &input_data->ls_datapaths->datapaths, > + &input_data->lr_datapaths->datapaths, > + &mcast_groups); > + ovn_igmp_groups_destroy(&igmp_groups); > } > > void > @@ -18675,218 +18243,6 @@ build_ip_mcast(struct ovsdb_idl_txn *ovnsb_txn, > } > } > > -static void > -build_mcast_groups(const struct sbrec_igmp_group_table > *sbrec_igmp_group_table, > - struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp, > - const struct ovn_datapaths *ls_datapaths, > - const struct hmap *ls_ports, > - const struct hmap *lr_ports, > - struct hmap *mcast_groups, > - struct hmap *igmp_groups) > -{ > - struct ovn_port *op; > - > - hmap_init(mcast_groups); > - hmap_init(igmp_groups); > - struct ovn_datapath *od; > - > - HMAP_FOR_EACH (od, key_node, &ls_datapaths->datapaths) { > - init_mcast_flow_count(od); > - } > - > - HMAP_FOR_EACH (op, key_node, lr_ports) { > - if (lrport_is_enabled(op->nbrp)) { > - /* If this port is configured to always flood multicast traffic > - * add it to the MC_STATIC group. > - */ > - if (op->mcast_info.flood) { > - ovn_multicast_add(mcast_groups, &mc_static, op); > - op->od->mcast_info.rtr.flood_static = true; > - } > - } > - } > - > - HMAP_FOR_EACH (op, key_node, ls_ports) { > - if (lsp_is_enabled(op->nbsp)) { > - ovn_multicast_add(mcast_groups, &mc_flood, op); > - > - if (!lsp_is_router(op->nbsp)) { > - ovn_multicast_add(mcast_groups, &mc_flood_l2, op); > - } > - > - if (op->has_unknown) { > - ovn_multicast_add(mcast_groups, &mc_unknown, op); > - } > - > - /* If this port is connected to a multicast router then add it > - * to the MC_MROUTER_FLOOD group. > - */ > - if (op->od->mcast_info.sw.flood_relay && op->peer && > - op->peer->od && op->peer->od->mcast_info.rtr.relay) { > - ovn_multicast_add(mcast_groups, &mc_mrouter_flood, op); > - } > - > - /* If this port is configured to always flood multicast reports > - * add it to the MC_MROUTER_FLOOD group (all reports must be > - * flooded to statically configured or learned mrouters). > - */ > - if (op->mcast_info.flood_reports) { > - ovn_multicast_add(mcast_groups, &mc_mrouter_flood, op); > - op->od->mcast_info.sw.flood_reports = true; > - } > - > - /* If this port is configured to always flood multicast traffic > - * add it to the MC_STATIC group. > - */ > - if (op->mcast_info.flood) { > - ovn_multicast_add(mcast_groups, &mc_static, op); > - op->od->mcast_info.sw.flood_static = true; > - } > - } > - } > - > - const struct sbrec_igmp_group *sb_igmp; > - > - SBREC_IGMP_GROUP_TABLE_FOR_EACH_SAFE (sb_igmp, sbrec_igmp_group_table) { > - /* If this is a stale group (e.g., controller had crashed, > - * purge it). > - */ > - if (!sb_igmp->chassis || !sb_igmp->datapath) { > - sbrec_igmp_group_delete(sb_igmp); > - continue; > - } > - > - /* If the datapath value is stale, purge the group. */ > - od = ovn_datapath_from_sbrec(&ls_datapaths->datapaths, NULL, > - sb_igmp->datapath); > - > - if (!od || ovn_datapath_is_stale(od)) { > - sbrec_igmp_group_delete(sb_igmp); > - continue; > - } > - > - struct in6_addr group_address; > - if (!strcmp(sb_igmp->address, OVN_IGMP_GROUP_MROUTERS)) { > - /* Use all-zeros IP to denote a group corresponding to mrouters. > */ > - memset(&group_address, 0, sizeof group_address); > - } else if (!ip46_parse(sb_igmp->address, &group_address)) { > - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); > - VLOG_WARN_RL(&rl, "invalid IGMP group address: %s", > - sb_igmp->address); > - continue; > - } > - > - /* Extract the IGMP group ports from the SB entry. */ > - size_t n_igmp_ports; > - struct ovn_port **igmp_ports = > - ovn_igmp_group_get_ports(sb_igmp, &n_igmp_ports, ls_ports); > - > - /* It can be that all ports in the IGMP group record already have > - * mcast_flood=true and then we can skip the group completely. > - */ > - if (!igmp_ports) { > - continue; > - } > - > - /* Add the IGMP group entry. Will also try to allocate an ID for it > - * if the multicast group already exists. > - */ > - struct ovn_igmp_group *igmp_group = > - ovn_igmp_group_add(sbrec_mcast_group_by_name_dp, igmp_groups, od, > - &group_address, sb_igmp->address); > - > - /* Add the extracted ports to the IGMP group. */ > - ovn_igmp_group_add_entry(igmp_group, igmp_ports, n_igmp_ports); > - } > - > - /* Build IGMP groups for multicast routers with relay enabled. The router > - * IGMP groups are based on the groups learnt by their multicast enabled > - * peers. > - */ > - HMAP_FOR_EACH (od, key_node, &ls_datapaths->datapaths) { > - > - if (ovs_list_is_empty(&od->mcast_info.groups)) { > - continue; > - } > - > - for (size_t i = 0; i < od->n_router_ports; i++) { > - struct ovn_port *router_port = od->router_ports[i]->peer; > - > - /* If the router the port connects to doesn't have multicast > - * relay enabled or if it was already configured to flood > - * multicast traffic then skip it. > - */ > - if (!router_port || !router_port->od || > - !router_port->od->mcast_info.rtr.relay || > - router_port->mcast_info.flood) { > - continue; > - } > - > - struct ovn_igmp_group *igmp_group; > - LIST_FOR_EACH (igmp_group, list_node, &od->mcast_info.groups) { > - struct in6_addr *address = &igmp_group->address; > - > - /* Skip mrouter entries. */ > - if (!strcmp(igmp_group->mcgroup.name, > - OVN_IGMP_GROUP_MROUTERS)) { > - continue; > - } > - > - /* For IPv6 only relay routable multicast groups > - * (RFC 4291 2.7). > - */ > - if (!IN6_IS_ADDR_V4MAPPED(address) && > - !ipv6_addr_is_routable_multicast(address)) { > - continue; > - } > - > - struct ovn_igmp_group *igmp_group_rtr = > - ovn_igmp_group_add(sbrec_mcast_group_by_name_dp, > - igmp_groups, router_port->od, > - address, igmp_group->mcgroup.name); > - struct ovn_port **router_igmp_ports = > - xmalloc(sizeof *router_igmp_ports); > - /* Store the chassis redirect port otherwise traffic will > not > - * be tunneled properly. > - */ > - router_igmp_ports[0] = router_port->cr_port > - ? router_port->cr_port > - : router_port; > - ovn_igmp_group_add_entry(igmp_group_rtr, router_igmp_ports, > 1); > - } > - } > - } > - > - /* Walk the aggregated IGMP groups and allocate IDs for new entries. > - * Then store the ports in the associated multicast group. > - * Mrouter entries are also stored as IGMP groups, deal with those > - * explicitly. > - */ > - struct ovn_igmp_group *igmp_group; > - HMAP_FOR_EACH_SAFE (igmp_group, hmap_node, igmp_groups) { > - > - /* If this is a mrouter entry just aggregate the mrouter ports > - * into the MC_MROUTER mcast_group and destroy the igmp_group; > - * no more processing needed. */ > - if (!strcmp(igmp_group->mcgroup.name, OVN_IGMP_GROUP_MROUTERS)) { > - ovn_igmp_mrouter_aggregate_ports(igmp_group, mcast_groups); > - ovn_igmp_group_destroy(igmp_groups, igmp_group); > - continue; > - } > - > - if (!ovn_igmp_group_allocate_id(igmp_group)) { > - /* If we ran out of keys just destroy the entry. */ > - ovn_igmp_group_destroy(igmp_groups, igmp_group); > - continue; > - } > - > - /* Aggregate the ports from all entries corresponding to this > - * group. > - */ > - ovn_igmp_group_aggregate_ports(igmp_group, mcast_groups); > - } > -} > - > static void > build_static_mac_binding_table( > struct ovsdb_idl_txn *ovnsb_txn, > diff --git a/northd/northd.h b/northd/northd.h > index 9457a7be6..044704c70 100644 > --- a/northd/northd.h > +++ b/northd/northd.h > @@ -874,4 +874,28 @@ is_vxlan_mode(const struct smap *nb_options, > > uint32_t get_ovn_max_dp_key_local(bool _vxlan_mode); > > +/* Returns true if the logical router port 'enabled' column is empty or > + * set to true. Otherwise, returns false. */ > +static inline bool > +lrport_is_enabled(const struct nbrec_logical_router_port *lrport) > +{ > + return !lrport->enabled || *lrport->enabled; > +} > + > +/* Returns true if the logical switch port 'enabled' column is empty or > + * set to true. Otherwise, returns false. */ > +static inline bool > +lsp_is_enabled(const struct nbrec_logical_switch_port *lsp) > +{ > + return !lsp->n_enabled || *lsp->enabled; > +} > + > +static inline bool > +lsp_is_router(const struct nbrec_logical_switch_port *nbsp) > +{ > + return !strcmp(nbsp->type, "router"); > +} > + > +struct ovn_port *ovn_port_find(const struct hmap *ports, const char *name); > + > #endif /* NORTHD_H */ > -- > 2.47.1 > > _______________________________________________ > dev mailing list > [email protected] > https://mail.openvswitch.org/mailman/listinfo/ovs-dev >
_______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
