162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci// Copyright (c) 2020, Nikolay Aleksandrov <nikolay@nvidia.com> 362306a36Sopenharmony_ci#include <linux/err.h> 462306a36Sopenharmony_ci#include <linux/export.h> 562306a36Sopenharmony_ci#include <linux/if_ether.h> 662306a36Sopenharmony_ci#include <linux/igmp.h> 762306a36Sopenharmony_ci#include <linux/in.h> 862306a36Sopenharmony_ci#include <linux/jhash.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/log2.h> 1162306a36Sopenharmony_ci#include <linux/netdevice.h> 1262306a36Sopenharmony_ci#include <linux/netfilter_bridge.h> 1362306a36Sopenharmony_ci#include <linux/random.h> 1462306a36Sopenharmony_ci#include <linux/rculist.h> 1562306a36Sopenharmony_ci#include <linux/skbuff.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/timer.h> 1862306a36Sopenharmony_ci#include <linux/inetdevice.h> 1962306a36Sopenharmony_ci#include <linux/mroute.h> 2062306a36Sopenharmony_ci#include <net/ip.h> 2162306a36Sopenharmony_ci#include <net/switchdev.h> 2262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 2362306a36Sopenharmony_ci#include <linux/icmpv6.h> 2462306a36Sopenharmony_ci#include <net/ipv6.h> 2562306a36Sopenharmony_ci#include <net/mld.h> 2662306a36Sopenharmony_ci#include <net/ip6_checksum.h> 2762306a36Sopenharmony_ci#include <net/addrconf.h> 2862306a36Sopenharmony_ci#endif 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "br_private.h" 3162306a36Sopenharmony_ci#include "br_private_mcast_eht.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic bool br_multicast_del_eht_set_entry(struct net_bridge_port_group *pg, 3462306a36Sopenharmony_ci union net_bridge_eht_addr *src_addr, 3562306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr); 3662306a36Sopenharmony_cistatic void br_multicast_create_eht_set_entry(const struct net_bridge_mcast *brmctx, 3762306a36Sopenharmony_ci struct net_bridge_port_group *pg, 3862306a36Sopenharmony_ci union net_bridge_eht_addr *src_addr, 3962306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr, 4062306a36Sopenharmony_ci int filter_mode, 4162306a36Sopenharmony_ci bool allow_zero_src); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct net_bridge_group_eht_host * 4462306a36Sopenharmony_cibr_multicast_eht_host_lookup(struct net_bridge_port_group *pg, 4562306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct rb_node *node = pg->eht_host_tree.rb_node; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci while (node) { 5062306a36Sopenharmony_ci struct net_bridge_group_eht_host *this; 5162306a36Sopenharmony_ci int result; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci this = rb_entry(node, struct net_bridge_group_eht_host, 5462306a36Sopenharmony_ci rb_node); 5562306a36Sopenharmony_ci result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr)); 5662306a36Sopenharmony_ci if (result < 0) 5762306a36Sopenharmony_ci node = node->rb_left; 5862306a36Sopenharmony_ci else if (result > 0) 5962306a36Sopenharmony_ci node = node->rb_right; 6062306a36Sopenharmony_ci else 6162306a36Sopenharmony_ci return this; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return NULL; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int br_multicast_eht_host_filter_mode(struct net_bridge_port_group *pg, 6862306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct net_bridge_group_eht_host *eht_host; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci eht_host = br_multicast_eht_host_lookup(pg, h_addr); 7362306a36Sopenharmony_ci if (!eht_host) 7462306a36Sopenharmony_ci return MCAST_INCLUDE; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return eht_host->filter_mode; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic struct net_bridge_group_eht_set_entry * 8062306a36Sopenharmony_cibr_multicast_eht_set_entry_lookup(struct net_bridge_group_eht_set *eht_set, 8162306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct rb_node *node = eht_set->entry_tree.rb_node; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci while (node) { 8662306a36Sopenharmony_ci struct net_bridge_group_eht_set_entry *this; 8762306a36Sopenharmony_ci int result; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci this = rb_entry(node, struct net_bridge_group_eht_set_entry, 9062306a36Sopenharmony_ci rb_node); 9162306a36Sopenharmony_ci result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr)); 9262306a36Sopenharmony_ci if (result < 0) 9362306a36Sopenharmony_ci node = node->rb_left; 9462306a36Sopenharmony_ci else if (result > 0) 9562306a36Sopenharmony_ci node = node->rb_right; 9662306a36Sopenharmony_ci else 9762306a36Sopenharmony_ci return this; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return NULL; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic struct net_bridge_group_eht_set * 10462306a36Sopenharmony_cibr_multicast_eht_set_lookup(struct net_bridge_port_group *pg, 10562306a36Sopenharmony_ci union net_bridge_eht_addr *src_addr) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct rb_node *node = pg->eht_set_tree.rb_node; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci while (node) { 11062306a36Sopenharmony_ci struct net_bridge_group_eht_set *this; 11162306a36Sopenharmony_ci int result; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci this = rb_entry(node, struct net_bridge_group_eht_set, 11462306a36Sopenharmony_ci rb_node); 11562306a36Sopenharmony_ci result = memcmp(src_addr, &this->src_addr, sizeof(*src_addr)); 11662306a36Sopenharmony_ci if (result < 0) 11762306a36Sopenharmony_ci node = node->rb_left; 11862306a36Sopenharmony_ci else if (result > 0) 11962306a36Sopenharmony_ci node = node->rb_right; 12062306a36Sopenharmony_ci else 12162306a36Sopenharmony_ci return this; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return NULL; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void __eht_destroy_host(struct net_bridge_group_eht_host *eht_host) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci WARN_ON(!hlist_empty(&eht_host->set_entries)); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci br_multicast_eht_hosts_dec(eht_host->pg); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci rb_erase(&eht_host->rb_node, &eht_host->pg->eht_host_tree); 13462306a36Sopenharmony_ci RB_CLEAR_NODE(&eht_host->rb_node); 13562306a36Sopenharmony_ci kfree(eht_host); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void br_multicast_destroy_eht_set_entry(struct net_bridge_mcast_gc *gc) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct net_bridge_group_eht_set_entry *set_h; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci set_h = container_of(gc, struct net_bridge_group_eht_set_entry, mcast_gc); 14362306a36Sopenharmony_ci WARN_ON(!RB_EMPTY_NODE(&set_h->rb_node)); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci timer_shutdown_sync(&set_h->timer); 14662306a36Sopenharmony_ci kfree(set_h); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void br_multicast_destroy_eht_set(struct net_bridge_mcast_gc *gc) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct net_bridge_group_eht_set *eht_set; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci eht_set = container_of(gc, struct net_bridge_group_eht_set, mcast_gc); 15462306a36Sopenharmony_ci WARN_ON(!RB_EMPTY_NODE(&eht_set->rb_node)); 15562306a36Sopenharmony_ci WARN_ON(!RB_EMPTY_ROOT(&eht_set->entry_tree)); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci timer_shutdown_sync(&eht_set->timer); 15862306a36Sopenharmony_ci kfree(eht_set); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void __eht_del_set_entry(struct net_bridge_group_eht_set_entry *set_h) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct net_bridge_group_eht_host *eht_host = set_h->h_parent; 16462306a36Sopenharmony_ci union net_bridge_eht_addr zero_addr; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci rb_erase(&set_h->rb_node, &set_h->eht_set->entry_tree); 16762306a36Sopenharmony_ci RB_CLEAR_NODE(&set_h->rb_node); 16862306a36Sopenharmony_ci hlist_del_init(&set_h->host_list); 16962306a36Sopenharmony_ci memset(&zero_addr, 0, sizeof(zero_addr)); 17062306a36Sopenharmony_ci if (memcmp(&set_h->h_addr, &zero_addr, sizeof(zero_addr))) 17162306a36Sopenharmony_ci eht_host->num_entries--; 17262306a36Sopenharmony_ci hlist_add_head(&set_h->mcast_gc.gc_node, &set_h->br->mcast_gc_list); 17362306a36Sopenharmony_ci queue_work(system_long_wq, &set_h->br->mcast_gc_work); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (hlist_empty(&eht_host->set_entries)) 17662306a36Sopenharmony_ci __eht_destroy_host(eht_host); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void br_multicast_del_eht_set(struct net_bridge_group_eht_set *eht_set) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct net_bridge_group_eht_set_entry *set_h; 18262306a36Sopenharmony_ci struct rb_node *node; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci while ((node = rb_first(&eht_set->entry_tree))) { 18562306a36Sopenharmony_ci set_h = rb_entry(node, struct net_bridge_group_eht_set_entry, 18662306a36Sopenharmony_ci rb_node); 18762306a36Sopenharmony_ci __eht_del_set_entry(set_h); 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci rb_erase(&eht_set->rb_node, &eht_set->pg->eht_set_tree); 19162306a36Sopenharmony_ci RB_CLEAR_NODE(&eht_set->rb_node); 19262306a36Sopenharmony_ci hlist_add_head(&eht_set->mcast_gc.gc_node, &eht_set->br->mcast_gc_list); 19362306a36Sopenharmony_ci queue_work(system_long_wq, &eht_set->br->mcast_gc_work); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_civoid br_multicast_eht_clean_sets(struct net_bridge_port_group *pg) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct net_bridge_group_eht_set *eht_set; 19962306a36Sopenharmony_ci struct rb_node *node; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci while ((node = rb_first(&pg->eht_set_tree))) { 20262306a36Sopenharmony_ci eht_set = rb_entry(node, struct net_bridge_group_eht_set, 20362306a36Sopenharmony_ci rb_node); 20462306a36Sopenharmony_ci br_multicast_del_eht_set(eht_set); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void br_multicast_eht_set_entry_expired(struct timer_list *t) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct net_bridge_group_eht_set_entry *set_h = from_timer(set_h, t, timer); 21162306a36Sopenharmony_ci struct net_bridge *br = set_h->br; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci spin_lock(&br->multicast_lock); 21462306a36Sopenharmony_ci if (RB_EMPTY_NODE(&set_h->rb_node) || timer_pending(&set_h->timer)) 21562306a36Sopenharmony_ci goto out; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci br_multicast_del_eht_set_entry(set_h->eht_set->pg, 21862306a36Sopenharmony_ci &set_h->eht_set->src_addr, 21962306a36Sopenharmony_ci &set_h->h_addr); 22062306a36Sopenharmony_ciout: 22162306a36Sopenharmony_ci spin_unlock(&br->multicast_lock); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void br_multicast_eht_set_expired(struct timer_list *t) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct net_bridge_group_eht_set *eht_set = from_timer(eht_set, t, 22762306a36Sopenharmony_ci timer); 22862306a36Sopenharmony_ci struct net_bridge *br = eht_set->br; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci spin_lock(&br->multicast_lock); 23162306a36Sopenharmony_ci if (RB_EMPTY_NODE(&eht_set->rb_node) || timer_pending(&eht_set->timer)) 23262306a36Sopenharmony_ci goto out; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci br_multicast_del_eht_set(eht_set); 23562306a36Sopenharmony_ciout: 23662306a36Sopenharmony_ci spin_unlock(&br->multicast_lock); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic struct net_bridge_group_eht_host * 24062306a36Sopenharmony_ci__eht_lookup_create_host(struct net_bridge_port_group *pg, 24162306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr, 24262306a36Sopenharmony_ci unsigned char filter_mode) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct rb_node **link = &pg->eht_host_tree.rb_node, *parent = NULL; 24562306a36Sopenharmony_ci struct net_bridge_group_eht_host *eht_host; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci while (*link) { 24862306a36Sopenharmony_ci struct net_bridge_group_eht_host *this; 24962306a36Sopenharmony_ci int result; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci this = rb_entry(*link, struct net_bridge_group_eht_host, 25262306a36Sopenharmony_ci rb_node); 25362306a36Sopenharmony_ci result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr)); 25462306a36Sopenharmony_ci parent = *link; 25562306a36Sopenharmony_ci if (result < 0) 25662306a36Sopenharmony_ci link = &((*link)->rb_left); 25762306a36Sopenharmony_ci else if (result > 0) 25862306a36Sopenharmony_ci link = &((*link)->rb_right); 25962306a36Sopenharmony_ci else 26062306a36Sopenharmony_ci return this; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (br_multicast_eht_hosts_over_limit(pg)) 26462306a36Sopenharmony_ci return NULL; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci eht_host = kzalloc(sizeof(*eht_host), GFP_ATOMIC); 26762306a36Sopenharmony_ci if (!eht_host) 26862306a36Sopenharmony_ci return NULL; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci memcpy(&eht_host->h_addr, h_addr, sizeof(*h_addr)); 27162306a36Sopenharmony_ci INIT_HLIST_HEAD(&eht_host->set_entries); 27262306a36Sopenharmony_ci eht_host->pg = pg; 27362306a36Sopenharmony_ci eht_host->filter_mode = filter_mode; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci rb_link_node(&eht_host->rb_node, parent, link); 27662306a36Sopenharmony_ci rb_insert_color(&eht_host->rb_node, &pg->eht_host_tree); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci br_multicast_eht_hosts_inc(pg); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return eht_host; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic struct net_bridge_group_eht_set_entry * 28462306a36Sopenharmony_ci__eht_lookup_create_set_entry(struct net_bridge *br, 28562306a36Sopenharmony_ci struct net_bridge_group_eht_set *eht_set, 28662306a36Sopenharmony_ci struct net_bridge_group_eht_host *eht_host, 28762306a36Sopenharmony_ci bool allow_zero_src) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct rb_node **link = &eht_set->entry_tree.rb_node, *parent = NULL; 29062306a36Sopenharmony_ci struct net_bridge_group_eht_set_entry *set_h; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci while (*link) { 29362306a36Sopenharmony_ci struct net_bridge_group_eht_set_entry *this; 29462306a36Sopenharmony_ci int result; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci this = rb_entry(*link, struct net_bridge_group_eht_set_entry, 29762306a36Sopenharmony_ci rb_node); 29862306a36Sopenharmony_ci result = memcmp(&eht_host->h_addr, &this->h_addr, 29962306a36Sopenharmony_ci sizeof(union net_bridge_eht_addr)); 30062306a36Sopenharmony_ci parent = *link; 30162306a36Sopenharmony_ci if (result < 0) 30262306a36Sopenharmony_ci link = &((*link)->rb_left); 30362306a36Sopenharmony_ci else if (result > 0) 30462306a36Sopenharmony_ci link = &((*link)->rb_right); 30562306a36Sopenharmony_ci else 30662306a36Sopenharmony_ci return this; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* always allow auto-created zero entry */ 31062306a36Sopenharmony_ci if (!allow_zero_src && eht_host->num_entries >= PG_SRC_ENT_LIMIT) 31162306a36Sopenharmony_ci return NULL; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci set_h = kzalloc(sizeof(*set_h), GFP_ATOMIC); 31462306a36Sopenharmony_ci if (!set_h) 31562306a36Sopenharmony_ci return NULL; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci memcpy(&set_h->h_addr, &eht_host->h_addr, 31862306a36Sopenharmony_ci sizeof(union net_bridge_eht_addr)); 31962306a36Sopenharmony_ci set_h->mcast_gc.destroy = br_multicast_destroy_eht_set_entry; 32062306a36Sopenharmony_ci set_h->eht_set = eht_set; 32162306a36Sopenharmony_ci set_h->h_parent = eht_host; 32262306a36Sopenharmony_ci set_h->br = br; 32362306a36Sopenharmony_ci timer_setup(&set_h->timer, br_multicast_eht_set_entry_expired, 0); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci hlist_add_head(&set_h->host_list, &eht_host->set_entries); 32662306a36Sopenharmony_ci rb_link_node(&set_h->rb_node, parent, link); 32762306a36Sopenharmony_ci rb_insert_color(&set_h->rb_node, &eht_set->entry_tree); 32862306a36Sopenharmony_ci /* we must not count the auto-created zero entry otherwise we won't be 32962306a36Sopenharmony_ci * able to track the full list of PG_SRC_ENT_LIMIT entries 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ci if (!allow_zero_src) 33262306a36Sopenharmony_ci eht_host->num_entries++; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return set_h; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic struct net_bridge_group_eht_set * 33862306a36Sopenharmony_ci__eht_lookup_create_set(struct net_bridge_port_group *pg, 33962306a36Sopenharmony_ci union net_bridge_eht_addr *src_addr) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct rb_node **link = &pg->eht_set_tree.rb_node, *parent = NULL; 34262306a36Sopenharmony_ci struct net_bridge_group_eht_set *eht_set; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci while (*link) { 34562306a36Sopenharmony_ci struct net_bridge_group_eht_set *this; 34662306a36Sopenharmony_ci int result; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci this = rb_entry(*link, struct net_bridge_group_eht_set, 34962306a36Sopenharmony_ci rb_node); 35062306a36Sopenharmony_ci result = memcmp(src_addr, &this->src_addr, sizeof(*src_addr)); 35162306a36Sopenharmony_ci parent = *link; 35262306a36Sopenharmony_ci if (result < 0) 35362306a36Sopenharmony_ci link = &((*link)->rb_left); 35462306a36Sopenharmony_ci else if (result > 0) 35562306a36Sopenharmony_ci link = &((*link)->rb_right); 35662306a36Sopenharmony_ci else 35762306a36Sopenharmony_ci return this; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci eht_set = kzalloc(sizeof(*eht_set), GFP_ATOMIC); 36162306a36Sopenharmony_ci if (!eht_set) 36262306a36Sopenharmony_ci return NULL; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci memcpy(&eht_set->src_addr, src_addr, sizeof(*src_addr)); 36562306a36Sopenharmony_ci eht_set->mcast_gc.destroy = br_multicast_destroy_eht_set; 36662306a36Sopenharmony_ci eht_set->pg = pg; 36762306a36Sopenharmony_ci eht_set->br = pg->key.port->br; 36862306a36Sopenharmony_ci eht_set->entry_tree = RB_ROOT; 36962306a36Sopenharmony_ci timer_setup(&eht_set->timer, br_multicast_eht_set_expired, 0); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci rb_link_node(&eht_set->rb_node, parent, link); 37262306a36Sopenharmony_ci rb_insert_color(&eht_set->rb_node, &pg->eht_set_tree); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return eht_set; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic void br_multicast_ip_src_to_eht_addr(const struct br_ip *src, 37862306a36Sopenharmony_ci union net_bridge_eht_addr *dest) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci switch (src->proto) { 38162306a36Sopenharmony_ci case htons(ETH_P_IP): 38262306a36Sopenharmony_ci dest->ip4 = src->src.ip4; 38362306a36Sopenharmony_ci break; 38462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 38562306a36Sopenharmony_ci case htons(ETH_P_IPV6): 38662306a36Sopenharmony_ci memcpy(&dest->ip6, &src->src.ip6, sizeof(struct in6_addr)); 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci#endif 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic void br_eht_convert_host_filter_mode(const struct net_bridge_mcast *brmctx, 39362306a36Sopenharmony_ci struct net_bridge_port_group *pg, 39462306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr, 39562306a36Sopenharmony_ci int filter_mode) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct net_bridge_group_eht_host *eht_host; 39862306a36Sopenharmony_ci union net_bridge_eht_addr zero_addr; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci eht_host = br_multicast_eht_host_lookup(pg, h_addr); 40162306a36Sopenharmony_ci if (eht_host) 40262306a36Sopenharmony_ci eht_host->filter_mode = filter_mode; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci memset(&zero_addr, 0, sizeof(zero_addr)); 40562306a36Sopenharmony_ci switch (filter_mode) { 40662306a36Sopenharmony_ci case MCAST_INCLUDE: 40762306a36Sopenharmony_ci br_multicast_del_eht_set_entry(pg, &zero_addr, h_addr); 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci case MCAST_EXCLUDE: 41062306a36Sopenharmony_ci br_multicast_create_eht_set_entry(brmctx, pg, &zero_addr, 41162306a36Sopenharmony_ci h_addr, MCAST_EXCLUDE, 41262306a36Sopenharmony_ci true); 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic void br_multicast_create_eht_set_entry(const struct net_bridge_mcast *brmctx, 41862306a36Sopenharmony_ci struct net_bridge_port_group *pg, 41962306a36Sopenharmony_ci union net_bridge_eht_addr *src_addr, 42062306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr, 42162306a36Sopenharmony_ci int filter_mode, 42262306a36Sopenharmony_ci bool allow_zero_src) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct net_bridge_group_eht_set_entry *set_h; 42562306a36Sopenharmony_ci struct net_bridge_group_eht_host *eht_host; 42662306a36Sopenharmony_ci struct net_bridge *br = pg->key.port->br; 42762306a36Sopenharmony_ci struct net_bridge_group_eht_set *eht_set; 42862306a36Sopenharmony_ci union net_bridge_eht_addr zero_addr; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci memset(&zero_addr, 0, sizeof(zero_addr)); 43162306a36Sopenharmony_ci if (!allow_zero_src && !memcmp(src_addr, &zero_addr, sizeof(zero_addr))) 43262306a36Sopenharmony_ci return; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci eht_set = __eht_lookup_create_set(pg, src_addr); 43562306a36Sopenharmony_ci if (!eht_set) 43662306a36Sopenharmony_ci return; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci eht_host = __eht_lookup_create_host(pg, h_addr, filter_mode); 43962306a36Sopenharmony_ci if (!eht_host) 44062306a36Sopenharmony_ci goto fail_host; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci set_h = __eht_lookup_create_set_entry(br, eht_set, eht_host, 44362306a36Sopenharmony_ci allow_zero_src); 44462306a36Sopenharmony_ci if (!set_h) 44562306a36Sopenharmony_ci goto fail_set_entry; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci mod_timer(&set_h->timer, jiffies + br_multicast_gmi(brmctx)); 44862306a36Sopenharmony_ci mod_timer(&eht_set->timer, jiffies + br_multicast_gmi(brmctx)); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cifail_set_entry: 45362306a36Sopenharmony_ci if (hlist_empty(&eht_host->set_entries)) 45462306a36Sopenharmony_ci __eht_destroy_host(eht_host); 45562306a36Sopenharmony_cifail_host: 45662306a36Sopenharmony_ci if (RB_EMPTY_ROOT(&eht_set->entry_tree)) 45762306a36Sopenharmony_ci br_multicast_del_eht_set(eht_set); 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic bool br_multicast_del_eht_set_entry(struct net_bridge_port_group *pg, 46162306a36Sopenharmony_ci union net_bridge_eht_addr *src_addr, 46262306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct net_bridge_group_eht_set_entry *set_h; 46562306a36Sopenharmony_ci struct net_bridge_group_eht_set *eht_set; 46662306a36Sopenharmony_ci bool set_deleted = false; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci eht_set = br_multicast_eht_set_lookup(pg, src_addr); 46962306a36Sopenharmony_ci if (!eht_set) 47062306a36Sopenharmony_ci goto out; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci set_h = br_multicast_eht_set_entry_lookup(eht_set, h_addr); 47362306a36Sopenharmony_ci if (!set_h) 47462306a36Sopenharmony_ci goto out; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci __eht_del_set_entry(set_h); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (RB_EMPTY_ROOT(&eht_set->entry_tree)) { 47962306a36Sopenharmony_ci br_multicast_del_eht_set(eht_set); 48062306a36Sopenharmony_ci set_deleted = true; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ciout: 48462306a36Sopenharmony_ci return set_deleted; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic void br_multicast_del_eht_host(struct net_bridge_port_group *pg, 48862306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct net_bridge_group_eht_set_entry *set_h; 49162306a36Sopenharmony_ci struct net_bridge_group_eht_host *eht_host; 49262306a36Sopenharmony_ci struct hlist_node *tmp; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci eht_host = br_multicast_eht_host_lookup(pg, h_addr); 49562306a36Sopenharmony_ci if (!eht_host) 49662306a36Sopenharmony_ci return; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci hlist_for_each_entry_safe(set_h, tmp, &eht_host->set_entries, host_list) 49962306a36Sopenharmony_ci br_multicast_del_eht_set_entry(set_h->eht_set->pg, 50062306a36Sopenharmony_ci &set_h->eht_set->src_addr, 50162306a36Sopenharmony_ci &set_h->h_addr); 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/* create new set entries from reports */ 50562306a36Sopenharmony_cistatic void __eht_create_set_entries(const struct net_bridge_mcast *brmctx, 50662306a36Sopenharmony_ci struct net_bridge_port_group *pg, 50762306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr, 50862306a36Sopenharmony_ci void *srcs, 50962306a36Sopenharmony_ci u32 nsrcs, 51062306a36Sopenharmony_ci size_t addr_size, 51162306a36Sopenharmony_ci int filter_mode) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci union net_bridge_eht_addr eht_src_addr; 51462306a36Sopenharmony_ci u32 src_idx; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci memset(&eht_src_addr, 0, sizeof(eht_src_addr)); 51762306a36Sopenharmony_ci for (src_idx = 0; src_idx < nsrcs; src_idx++) { 51862306a36Sopenharmony_ci memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size); 51962306a36Sopenharmony_ci br_multicast_create_eht_set_entry(brmctx, pg, &eht_src_addr, 52062306a36Sopenharmony_ci h_addr, filter_mode, 52162306a36Sopenharmony_ci false); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci/* delete existing set entries and their (S,G) entries if they were the last */ 52662306a36Sopenharmony_cistatic bool __eht_del_set_entries(struct net_bridge_port_group *pg, 52762306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr, 52862306a36Sopenharmony_ci void *srcs, 52962306a36Sopenharmony_ci u32 nsrcs, 53062306a36Sopenharmony_ci size_t addr_size) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci union net_bridge_eht_addr eht_src_addr; 53362306a36Sopenharmony_ci struct net_bridge_group_src *src_ent; 53462306a36Sopenharmony_ci bool changed = false; 53562306a36Sopenharmony_ci struct br_ip src_ip; 53662306a36Sopenharmony_ci u32 src_idx; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci memset(&eht_src_addr, 0, sizeof(eht_src_addr)); 53962306a36Sopenharmony_ci memset(&src_ip, 0, sizeof(src_ip)); 54062306a36Sopenharmony_ci src_ip.proto = pg->key.addr.proto; 54162306a36Sopenharmony_ci for (src_idx = 0; src_idx < nsrcs; src_idx++) { 54262306a36Sopenharmony_ci memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size); 54362306a36Sopenharmony_ci if (!br_multicast_del_eht_set_entry(pg, &eht_src_addr, h_addr)) 54462306a36Sopenharmony_ci continue; 54562306a36Sopenharmony_ci memcpy(&src_ip, srcs + (src_idx * addr_size), addr_size); 54662306a36Sopenharmony_ci src_ent = br_multicast_find_group_src(pg, &src_ip); 54762306a36Sopenharmony_ci if (!src_ent) 54862306a36Sopenharmony_ci continue; 54962306a36Sopenharmony_ci br_multicast_del_group_src(src_ent, true); 55062306a36Sopenharmony_ci changed = true; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return changed; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic bool br_multicast_eht_allow(const struct net_bridge_mcast *brmctx, 55762306a36Sopenharmony_ci struct net_bridge_port_group *pg, 55862306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr, 55962306a36Sopenharmony_ci void *srcs, 56062306a36Sopenharmony_ci u32 nsrcs, 56162306a36Sopenharmony_ci size_t addr_size) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci bool changed = false; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci switch (br_multicast_eht_host_filter_mode(pg, h_addr)) { 56662306a36Sopenharmony_ci case MCAST_INCLUDE: 56762306a36Sopenharmony_ci __eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs, 56862306a36Sopenharmony_ci addr_size, MCAST_INCLUDE); 56962306a36Sopenharmony_ci break; 57062306a36Sopenharmony_ci case MCAST_EXCLUDE: 57162306a36Sopenharmony_ci changed = __eht_del_set_entries(pg, h_addr, srcs, nsrcs, 57262306a36Sopenharmony_ci addr_size); 57362306a36Sopenharmony_ci break; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return changed; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic bool br_multicast_eht_block(const struct net_bridge_mcast *brmctx, 58062306a36Sopenharmony_ci struct net_bridge_port_group *pg, 58162306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr, 58262306a36Sopenharmony_ci void *srcs, 58362306a36Sopenharmony_ci u32 nsrcs, 58462306a36Sopenharmony_ci size_t addr_size) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci bool changed = false; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci switch (br_multicast_eht_host_filter_mode(pg, h_addr)) { 58962306a36Sopenharmony_ci case MCAST_INCLUDE: 59062306a36Sopenharmony_ci changed = __eht_del_set_entries(pg, h_addr, srcs, nsrcs, 59162306a36Sopenharmony_ci addr_size); 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci case MCAST_EXCLUDE: 59462306a36Sopenharmony_ci __eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 59562306a36Sopenharmony_ci MCAST_EXCLUDE); 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return changed; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci/* flush_entries is true when changing mode */ 60362306a36Sopenharmony_cistatic bool __eht_inc_exc(const struct net_bridge_mcast *brmctx, 60462306a36Sopenharmony_ci struct net_bridge_port_group *pg, 60562306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr, 60662306a36Sopenharmony_ci void *srcs, 60762306a36Sopenharmony_ci u32 nsrcs, 60862306a36Sopenharmony_ci size_t addr_size, 60962306a36Sopenharmony_ci unsigned char filter_mode, 61062306a36Sopenharmony_ci bool to_report) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci bool changed = false, flush_entries = to_report; 61362306a36Sopenharmony_ci union net_bridge_eht_addr eht_src_addr; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (br_multicast_eht_host_filter_mode(pg, h_addr) != filter_mode) 61662306a36Sopenharmony_ci flush_entries = true; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci memset(&eht_src_addr, 0, sizeof(eht_src_addr)); 61962306a36Sopenharmony_ci /* if we're changing mode del host and its entries */ 62062306a36Sopenharmony_ci if (flush_entries) 62162306a36Sopenharmony_ci br_multicast_del_eht_host(pg, h_addr); 62262306a36Sopenharmony_ci __eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 62362306a36Sopenharmony_ci filter_mode); 62462306a36Sopenharmony_ci /* we can be missing sets only if we've deleted some entries */ 62562306a36Sopenharmony_ci if (flush_entries) { 62662306a36Sopenharmony_ci struct net_bridge_group_eht_set *eht_set; 62762306a36Sopenharmony_ci struct net_bridge_group_src *src_ent; 62862306a36Sopenharmony_ci struct hlist_node *tmp; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci hlist_for_each_entry_safe(src_ent, tmp, &pg->src_list, node) { 63162306a36Sopenharmony_ci br_multicast_ip_src_to_eht_addr(&src_ent->addr, 63262306a36Sopenharmony_ci &eht_src_addr); 63362306a36Sopenharmony_ci if (!br_multicast_eht_set_lookup(pg, &eht_src_addr)) { 63462306a36Sopenharmony_ci br_multicast_del_group_src(src_ent, true); 63562306a36Sopenharmony_ci changed = true; 63662306a36Sopenharmony_ci continue; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci /* this is an optimization for TO_INCLUDE where we lower 63962306a36Sopenharmony_ci * the set's timeout to LMQT to catch timeout hosts: 64062306a36Sopenharmony_ci * - host A (timing out): set entries X, Y 64162306a36Sopenharmony_ci * - host B: set entry Z (new from current TO_INCLUDE) 64262306a36Sopenharmony_ci * sends BLOCK Z after LMQT but host A's EHT 64362306a36Sopenharmony_ci * entries still exist (unless lowered to LMQT 64462306a36Sopenharmony_ci * so they can timeout with the S,Gs) 64562306a36Sopenharmony_ci * => we wait another LMQT, when we can just delete the 64662306a36Sopenharmony_ci * group immediately 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_ci if (!(src_ent->flags & BR_SGRP_F_SEND) || 64962306a36Sopenharmony_ci filter_mode != MCAST_INCLUDE || 65062306a36Sopenharmony_ci !to_report) 65162306a36Sopenharmony_ci continue; 65262306a36Sopenharmony_ci eht_set = br_multicast_eht_set_lookup(pg, 65362306a36Sopenharmony_ci &eht_src_addr); 65462306a36Sopenharmony_ci if (!eht_set) 65562306a36Sopenharmony_ci continue; 65662306a36Sopenharmony_ci mod_timer(&eht_set->timer, jiffies + br_multicast_lmqt(brmctx)); 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return changed; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic bool br_multicast_eht_inc(const struct net_bridge_mcast *brmctx, 66462306a36Sopenharmony_ci struct net_bridge_port_group *pg, 66562306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr, 66662306a36Sopenharmony_ci void *srcs, 66762306a36Sopenharmony_ci u32 nsrcs, 66862306a36Sopenharmony_ci size_t addr_size, 66962306a36Sopenharmony_ci bool to_report) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci bool changed; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci changed = __eht_inc_exc(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 67462306a36Sopenharmony_ci MCAST_INCLUDE, to_report); 67562306a36Sopenharmony_ci br_eht_convert_host_filter_mode(brmctx, pg, h_addr, MCAST_INCLUDE); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci return changed; 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic bool br_multicast_eht_exc(const struct net_bridge_mcast *brmctx, 68162306a36Sopenharmony_ci struct net_bridge_port_group *pg, 68262306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr, 68362306a36Sopenharmony_ci void *srcs, 68462306a36Sopenharmony_ci u32 nsrcs, 68562306a36Sopenharmony_ci size_t addr_size, 68662306a36Sopenharmony_ci bool to_report) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci bool changed; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci changed = __eht_inc_exc(brmctx, pg, h_addr, srcs, nsrcs, addr_size, 69162306a36Sopenharmony_ci MCAST_EXCLUDE, to_report); 69262306a36Sopenharmony_ci br_eht_convert_host_filter_mode(brmctx, pg, h_addr, MCAST_EXCLUDE); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci return changed; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic bool __eht_ip4_handle(const struct net_bridge_mcast *brmctx, 69862306a36Sopenharmony_ci struct net_bridge_port_group *pg, 69962306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr, 70062306a36Sopenharmony_ci void *srcs, 70162306a36Sopenharmony_ci u32 nsrcs, 70262306a36Sopenharmony_ci int grec_type) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci bool changed = false, to_report = false; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci switch (grec_type) { 70762306a36Sopenharmony_ci case IGMPV3_ALLOW_NEW_SOURCES: 70862306a36Sopenharmony_ci br_multicast_eht_allow(brmctx, pg, h_addr, srcs, nsrcs, 70962306a36Sopenharmony_ci sizeof(__be32)); 71062306a36Sopenharmony_ci break; 71162306a36Sopenharmony_ci case IGMPV3_BLOCK_OLD_SOURCES: 71262306a36Sopenharmony_ci changed = br_multicast_eht_block(brmctx, pg, h_addr, srcs, nsrcs, 71362306a36Sopenharmony_ci sizeof(__be32)); 71462306a36Sopenharmony_ci break; 71562306a36Sopenharmony_ci case IGMPV3_CHANGE_TO_INCLUDE: 71662306a36Sopenharmony_ci to_report = true; 71762306a36Sopenharmony_ci fallthrough; 71862306a36Sopenharmony_ci case IGMPV3_MODE_IS_INCLUDE: 71962306a36Sopenharmony_ci changed = br_multicast_eht_inc(brmctx, pg, h_addr, srcs, nsrcs, 72062306a36Sopenharmony_ci sizeof(__be32), to_report); 72162306a36Sopenharmony_ci break; 72262306a36Sopenharmony_ci case IGMPV3_CHANGE_TO_EXCLUDE: 72362306a36Sopenharmony_ci to_report = true; 72462306a36Sopenharmony_ci fallthrough; 72562306a36Sopenharmony_ci case IGMPV3_MODE_IS_EXCLUDE: 72662306a36Sopenharmony_ci changed = br_multicast_eht_exc(brmctx, pg, h_addr, srcs, nsrcs, 72762306a36Sopenharmony_ci sizeof(__be32), to_report); 72862306a36Sopenharmony_ci break; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return changed; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 73562306a36Sopenharmony_cistatic bool __eht_ip6_handle(const struct net_bridge_mcast *brmctx, 73662306a36Sopenharmony_ci struct net_bridge_port_group *pg, 73762306a36Sopenharmony_ci union net_bridge_eht_addr *h_addr, 73862306a36Sopenharmony_ci void *srcs, 73962306a36Sopenharmony_ci u32 nsrcs, 74062306a36Sopenharmony_ci int grec_type) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci bool changed = false, to_report = false; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci switch (grec_type) { 74562306a36Sopenharmony_ci case MLD2_ALLOW_NEW_SOURCES: 74662306a36Sopenharmony_ci br_multicast_eht_allow(brmctx, pg, h_addr, srcs, nsrcs, 74762306a36Sopenharmony_ci sizeof(struct in6_addr)); 74862306a36Sopenharmony_ci break; 74962306a36Sopenharmony_ci case MLD2_BLOCK_OLD_SOURCES: 75062306a36Sopenharmony_ci changed = br_multicast_eht_block(brmctx, pg, h_addr, srcs, nsrcs, 75162306a36Sopenharmony_ci sizeof(struct in6_addr)); 75262306a36Sopenharmony_ci break; 75362306a36Sopenharmony_ci case MLD2_CHANGE_TO_INCLUDE: 75462306a36Sopenharmony_ci to_report = true; 75562306a36Sopenharmony_ci fallthrough; 75662306a36Sopenharmony_ci case MLD2_MODE_IS_INCLUDE: 75762306a36Sopenharmony_ci changed = br_multicast_eht_inc(brmctx, pg, h_addr, srcs, nsrcs, 75862306a36Sopenharmony_ci sizeof(struct in6_addr), 75962306a36Sopenharmony_ci to_report); 76062306a36Sopenharmony_ci break; 76162306a36Sopenharmony_ci case MLD2_CHANGE_TO_EXCLUDE: 76262306a36Sopenharmony_ci to_report = true; 76362306a36Sopenharmony_ci fallthrough; 76462306a36Sopenharmony_ci case MLD2_MODE_IS_EXCLUDE: 76562306a36Sopenharmony_ci changed = br_multicast_eht_exc(brmctx, pg, h_addr, srcs, nsrcs, 76662306a36Sopenharmony_ci sizeof(struct in6_addr), 76762306a36Sopenharmony_ci to_report); 76862306a36Sopenharmony_ci break; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci return changed; 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci#endif 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci/* true means an entry was deleted */ 77662306a36Sopenharmony_cibool br_multicast_eht_handle(const struct net_bridge_mcast *brmctx, 77762306a36Sopenharmony_ci struct net_bridge_port_group *pg, 77862306a36Sopenharmony_ci void *h_addr, 77962306a36Sopenharmony_ci void *srcs, 78062306a36Sopenharmony_ci u32 nsrcs, 78162306a36Sopenharmony_ci size_t addr_size, 78262306a36Sopenharmony_ci int grec_type) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci bool eht_enabled = !!(pg->key.port->flags & BR_MULTICAST_FAST_LEAVE); 78562306a36Sopenharmony_ci union net_bridge_eht_addr eht_host_addr; 78662306a36Sopenharmony_ci bool changed = false; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (!eht_enabled) 78962306a36Sopenharmony_ci goto out; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci memset(&eht_host_addr, 0, sizeof(eht_host_addr)); 79262306a36Sopenharmony_ci memcpy(&eht_host_addr, h_addr, addr_size); 79362306a36Sopenharmony_ci if (addr_size == sizeof(__be32)) 79462306a36Sopenharmony_ci changed = __eht_ip4_handle(brmctx, pg, &eht_host_addr, srcs, 79562306a36Sopenharmony_ci nsrcs, grec_type); 79662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 79762306a36Sopenharmony_ci else 79862306a36Sopenharmony_ci changed = __eht_ip6_handle(brmctx, pg, &eht_host_addr, srcs, 79962306a36Sopenharmony_ci nsrcs, grec_type); 80062306a36Sopenharmony_ci#endif 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ciout: 80362306a36Sopenharmony_ci return changed; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ciint br_multicast_eht_set_hosts_limit(struct net_bridge_port *p, 80762306a36Sopenharmony_ci u32 eht_hosts_limit) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci struct net_bridge *br = p->br; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (!eht_hosts_limit) 81262306a36Sopenharmony_ci return -EINVAL; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci spin_lock_bh(&br->multicast_lock); 81562306a36Sopenharmony_ci p->multicast_eht_hosts_limit = eht_hosts_limit; 81662306a36Sopenharmony_ci spin_unlock_bh(&br->multicast_lock); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci return 0; 81962306a36Sopenharmony_ci} 820