File Index
Files and directories
Sign in
FILE

br_multicast_eht.c

File preview with syntax highlighting.

linux-6.19.14/net/bridge/br_multicast_eht.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2// Copyright (c) 2020, Nikolay Aleksandrov <nikolay@nvidia.com>
  3#include <linux/err.h>
  4#include <linux/export.h>
  5#include <linux/if_ether.h>
  6#include <linux/igmp.h>
  7#include <linux/in.h>
  8#include <linux/jhash.h>
  9#include <linux/kernel.h>
 10#include <linux/log2.h>
 11#include <linux/netdevice.h>
 12#include <linux/netfilter_bridge.h>
 13#include <linux/random.h>
 14#include <linux/rculist.h>
 15#include <linux/skbuff.h>
 16#include <linux/slab.h>
 17#include <linux/timer.h>
 18#include <linux/inetdevice.h>
 19#include <linux/mroute.h>
 20#include <net/ip.h>
 21#include <net/switchdev.h>
 22#if IS_ENABLED(CONFIG_IPV6)
 23#include <linux/icmpv6.h>
 24#include <net/ipv6.h>
 25#include <net/mld.h>
 26#include <net/ip6_checksum.h>
 27#include <net/addrconf.h>
 28#endif
 29
 30#include "br_private.h"
 31#include "br_private_mcast_eht.h"
 32
 33static bool br_multicast_del_eht_set_entry(struct net_bridge_port_group *pg,
 34					   union net_bridge_eht_addr *src_addr,
 35					   union net_bridge_eht_addr *h_addr);
 36static void br_multicast_create_eht_set_entry(const struct net_bridge_mcast *brmctx,
 37					      struct net_bridge_port_group *pg,
 38					      union net_bridge_eht_addr *src_addr,
 39					      union net_bridge_eht_addr *h_addr,
 40					      int filter_mode,
 41					      bool allow_zero_src);
 42
 43static struct net_bridge_group_eht_host *
 44br_multicast_eht_host_lookup(struct net_bridge_port_group *pg,
 45			     union net_bridge_eht_addr *h_addr)
 46{
 47	struct rb_node *node = pg->eht_host_tree.rb_node;
 48
 49	while (node) {
 50		struct net_bridge_group_eht_host *this;
 51		int result;
 52
 53		this = rb_entry(node, struct net_bridge_group_eht_host,
 54				rb_node);
 55		result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr));
 56		if (result < 0)
 57			node = node->rb_left;
 58		else if (result > 0)
 59			node = node->rb_right;
 60		else
 61			return this;
 62	}
 63
 64	return NULL;
 65}
 66
 67static int br_multicast_eht_host_filter_mode(struct net_bridge_port_group *pg,
 68					     union net_bridge_eht_addr *h_addr)
 69{
 70	struct net_bridge_group_eht_host *eht_host;
 71
 72	eht_host = br_multicast_eht_host_lookup(pg, h_addr);
 73	if (!eht_host)
 74		return MCAST_INCLUDE;
 75
 76	return eht_host->filter_mode;
 77}
 78
 79static struct net_bridge_group_eht_set_entry *
 80br_multicast_eht_set_entry_lookup(struct net_bridge_group_eht_set *eht_set,
 81				  union net_bridge_eht_addr *h_addr)
 82{
 83	struct rb_node *node = eht_set->entry_tree.rb_node;
 84
 85	while (node) {
 86		struct net_bridge_group_eht_set_entry *this;
 87		int result;
 88
 89		this = rb_entry(node, struct net_bridge_group_eht_set_entry,
 90				rb_node);
 91		result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr));
 92		if (result < 0)
 93			node = node->rb_left;
 94		else if (result > 0)
 95			node = node->rb_right;
 96		else
 97			return this;
 98	}
 99
100	return NULL;
101}
102
103static struct net_bridge_group_eht_set *
104br_multicast_eht_set_lookup(struct net_bridge_port_group *pg,
105			    union net_bridge_eht_addr *src_addr)
106{
107	struct rb_node *node = pg->eht_set_tree.rb_node;
108
109	while (node) {
110		struct net_bridge_group_eht_set *this;
111		int result;
112
113		this = rb_entry(node, struct net_bridge_group_eht_set,
114				rb_node);
115		result = memcmp(src_addr, &this->src_addr, sizeof(*src_addr));
116		if (result < 0)
117			node = node->rb_left;
118		else if (result > 0)
119			node = node->rb_right;
120		else
121			return this;
122	}
123
124	return NULL;
125}
126
127static void __eht_destroy_host(struct net_bridge_group_eht_host *eht_host)
128{
129	WARN_ON(!hlist_empty(&eht_host->set_entries));
130
131	br_multicast_eht_hosts_dec(eht_host->pg);
132
133	rb_erase(&eht_host->rb_node, &eht_host->pg->eht_host_tree);
134	RB_CLEAR_NODE(&eht_host->rb_node);
135	kfree(eht_host);
136}
137
138static void br_multicast_destroy_eht_set_entry(struct net_bridge_mcast_gc *gc)
139{
140	struct net_bridge_group_eht_set_entry *set_h;
141
142	set_h = container_of(gc, struct net_bridge_group_eht_set_entry, mcast_gc);
143	WARN_ON(!RB_EMPTY_NODE(&set_h->rb_node));
144
145	timer_shutdown_sync(&set_h->timer);
146	kfree(set_h);
147}
148
149static void br_multicast_destroy_eht_set(struct net_bridge_mcast_gc *gc)
150{
151	struct net_bridge_group_eht_set *eht_set;
152
153	eht_set = container_of(gc, struct net_bridge_group_eht_set, mcast_gc);
154	WARN_ON(!RB_EMPTY_NODE(&eht_set->rb_node));
155	WARN_ON(!RB_EMPTY_ROOT(&eht_set->entry_tree));
156
157	timer_shutdown_sync(&eht_set->timer);
158	kfree(eht_set);
159}
160
161static void __eht_del_set_entry(struct net_bridge_group_eht_set_entry *set_h)
162{
163	struct net_bridge_group_eht_host *eht_host = set_h->h_parent;
164	union net_bridge_eht_addr zero_addr;
165
166	rb_erase(&set_h->rb_node, &set_h->eht_set->entry_tree);
167	RB_CLEAR_NODE(&set_h->rb_node);
168	hlist_del_init(&set_h->host_list);
169	memset(&zero_addr, 0, sizeof(zero_addr));
170	if (memcmp(&set_h->h_addr, &zero_addr, sizeof(zero_addr)))
171		eht_host->num_entries--;
172	hlist_add_head(&set_h->mcast_gc.gc_node, &set_h->br->mcast_gc_list);
173	queue_work(system_long_wq, &set_h->br->mcast_gc_work);
174
175	if (hlist_empty(&eht_host->set_entries))
176		__eht_destroy_host(eht_host);
177}
178
179static void br_multicast_del_eht_set(struct net_bridge_group_eht_set *eht_set)
180{
181	struct net_bridge_group_eht_set_entry *set_h;
182	struct rb_node *node;
183
184	while ((node = rb_first(&eht_set->entry_tree))) {
185		set_h = rb_entry(node, struct net_bridge_group_eht_set_entry,
186				 rb_node);
187		__eht_del_set_entry(set_h);
188	}
189
190	rb_erase(&eht_set->rb_node, &eht_set->pg->eht_set_tree);
191	RB_CLEAR_NODE(&eht_set->rb_node);
192	hlist_add_head(&eht_set->mcast_gc.gc_node, &eht_set->br->mcast_gc_list);
193	queue_work(system_long_wq, &eht_set->br->mcast_gc_work);
194}
195
196void br_multicast_eht_clean_sets(struct net_bridge_port_group *pg)
197{
198	struct net_bridge_group_eht_set *eht_set;
199	struct rb_node *node;
200
201	while ((node = rb_first(&pg->eht_set_tree))) {
202		eht_set = rb_entry(node, struct net_bridge_group_eht_set,
203				   rb_node);
204		br_multicast_del_eht_set(eht_set);
205	}
206}
207
208static void br_multicast_eht_set_entry_expired(struct timer_list *t)
209{
210	struct net_bridge_group_eht_set_entry *set_h = timer_container_of(set_h,
211									  t,
212									  timer);
213	struct net_bridge *br = set_h->br;
214
215	spin_lock(&br->multicast_lock);
216	if (RB_EMPTY_NODE(&set_h->rb_node) || timer_pending(&set_h->timer))
217		goto out;
218
219	br_multicast_del_eht_set_entry(set_h->eht_set->pg,
220				       &set_h->eht_set->src_addr,
221				       &set_h->h_addr);
222out:
223	spin_unlock(&br->multicast_lock);
224}
225
226static void br_multicast_eht_set_expired(struct timer_list *t)
227{
228	struct net_bridge_group_eht_set *eht_set = timer_container_of(eht_set,
229								      t,
230								      timer);
231	struct net_bridge *br = eht_set->br;
232
233	spin_lock(&br->multicast_lock);
234	if (RB_EMPTY_NODE(&eht_set->rb_node) || timer_pending(&eht_set->timer))
235		goto out;
236
237	br_multicast_del_eht_set(eht_set);
238out:
239	spin_unlock(&br->multicast_lock);
240}
241
242static struct net_bridge_group_eht_host *
243__eht_lookup_create_host(struct net_bridge_port_group *pg,
244			 union net_bridge_eht_addr *h_addr,
245			 unsigned char filter_mode)
246{
247	struct rb_node **link = &pg->eht_host_tree.rb_node, *parent = NULL;
248	struct net_bridge_group_eht_host *eht_host;
249
250	while (*link) {
251		struct net_bridge_group_eht_host *this;
252		int result;
253
254		this = rb_entry(*link, struct net_bridge_group_eht_host,
255				rb_node);
256		result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr));
257		parent = *link;
258		if (result < 0)
259			link = &((*link)->rb_left);
260		else if (result > 0)
261			link = &((*link)->rb_right);
262		else
263			return this;
264	}
265
266	if (br_multicast_eht_hosts_over_limit(pg))
267		return NULL;
268
269	eht_host = kzalloc(sizeof(*eht_host), GFP_ATOMIC);
270	if (!eht_host)
271		return NULL;
272
273	memcpy(&eht_host->h_addr, h_addr, sizeof(*h_addr));
274	INIT_HLIST_HEAD(&eht_host->set_entries);
275	eht_host->pg = pg;
276	eht_host->filter_mode = filter_mode;
277
278	rb_link_node(&eht_host->rb_node, parent, link);
279	rb_insert_color(&eht_host->rb_node, &pg->eht_host_tree);
280
281	br_multicast_eht_hosts_inc(pg);
282
283	return eht_host;
284}
285
286static struct net_bridge_group_eht_set_entry *
287__eht_lookup_create_set_entry(struct net_bridge *br,
288			      struct net_bridge_group_eht_set *eht_set,
289			      struct net_bridge_group_eht_host *eht_host,
290			      bool allow_zero_src)
291{
292	struct rb_node **link = &eht_set->entry_tree.rb_node, *parent = NULL;
293	struct net_bridge_group_eht_set_entry *set_h;
294
295	while (*link) {
296		struct net_bridge_group_eht_set_entry *this;
297		int result;
298
299		this = rb_entry(*link, struct net_bridge_group_eht_set_entry,
300				rb_node);
301		result = memcmp(&eht_host->h_addr, &this->h_addr,
302				sizeof(union net_bridge_eht_addr));
303		parent = *link;
304		if (result < 0)
305			link = &((*link)->rb_left);
306		else if (result > 0)
307			link = &((*link)->rb_right);
308		else
309			return this;
310	}
311
312	/* always allow auto-created zero entry */
313	if (!allow_zero_src && eht_host->num_entries >= PG_SRC_ENT_LIMIT)
314		return NULL;
315
316	set_h = kzalloc(sizeof(*set_h), GFP_ATOMIC);
317	if (!set_h)
318		return NULL;
319
320	memcpy(&set_h->h_addr, &eht_host->h_addr,
321	       sizeof(union net_bridge_eht_addr));
322	set_h->mcast_gc.destroy = br_multicast_destroy_eht_set_entry;
323	set_h->eht_set = eht_set;
324	set_h->h_parent = eht_host;
325	set_h->br = br;
326	timer_setup(&set_h->timer, br_multicast_eht_set_entry_expired, 0);
327
328	hlist_add_head(&set_h->host_list, &eht_host->set_entries);
329	rb_link_node(&set_h->rb_node, parent, link);
330	rb_insert_color(&set_h->rb_node, &eht_set->entry_tree);
331	/* we must not count the auto-created zero entry otherwise we won't be
332	 * able to track the full list of PG_SRC_ENT_LIMIT entries
333	 */
334	if (!allow_zero_src)
335		eht_host->num_entries++;
336
337	return set_h;
338}
339
340static struct net_bridge_group_eht_set *
341__eht_lookup_create_set(struct net_bridge_port_group *pg,
342			union net_bridge_eht_addr *src_addr)
343{
344	struct rb_node **link = &pg->eht_set_tree.rb_node, *parent = NULL;
345	struct net_bridge_group_eht_set *eht_set;
346
347	while (*link) {
348		struct net_bridge_group_eht_set *this;
349		int result;
350
351		this = rb_entry(*link, struct net_bridge_group_eht_set,
352				rb_node);
353		result = memcmp(src_addr, &this->src_addr, sizeof(*src_addr));
354		parent = *link;
355		if (result < 0)
356			link = &((*link)->rb_left);
357		else if (result > 0)
358			link = &((*link)->rb_right);
359		else
360			return this;
361	}
362
363	eht_set = kzalloc(sizeof(*eht_set), GFP_ATOMIC);
364	if (!eht_set)
365		return NULL;
366
367	memcpy(&eht_set->src_addr, src_addr, sizeof(*src_addr));
368	eht_set->mcast_gc.destroy = br_multicast_destroy_eht_set;
369	eht_set->pg = pg;
370	eht_set->br = pg->key.port->br;
371	eht_set->entry_tree = RB_ROOT;
372	timer_setup(&eht_set->timer, br_multicast_eht_set_expired, 0);
373
374	rb_link_node(&eht_set->rb_node, parent, link);
375	rb_insert_color(&eht_set->rb_node, &pg->eht_set_tree);
376
377	return eht_set;
378}
379
380static void br_multicast_ip_src_to_eht_addr(const struct br_ip *src,
381					    union net_bridge_eht_addr *dest)
382{
383	switch (src->proto) {
384	case htons(ETH_P_IP):
385		dest->ip4 = src->src.ip4;
386		break;
387#if IS_ENABLED(CONFIG_IPV6)
388	case htons(ETH_P_IPV6):
389		memcpy(&dest->ip6, &src->src.ip6, sizeof(struct in6_addr));
390		break;
391#endif
392	}
393}
394
395static void br_eht_convert_host_filter_mode(const struct net_bridge_mcast *brmctx,
396					    struct net_bridge_port_group *pg,
397					    union net_bridge_eht_addr *h_addr,
398					    int filter_mode)
399{
400	struct net_bridge_group_eht_host *eht_host;
401	union net_bridge_eht_addr zero_addr;
402
403	eht_host = br_multicast_eht_host_lookup(pg, h_addr);
404	if (eht_host)
405		eht_host->filter_mode = filter_mode;
406
407	memset(&zero_addr, 0, sizeof(zero_addr));
408	switch (filter_mode) {
409	case MCAST_INCLUDE:
410		br_multicast_del_eht_set_entry(pg, &zero_addr, h_addr);
411		break;
412	case MCAST_EXCLUDE:
413		br_multicast_create_eht_set_entry(brmctx, pg, &zero_addr,
414						  h_addr, MCAST_EXCLUDE,
415						  true);
416		break;
417	}
418}
419
420static void br_multicast_create_eht_set_entry(const struct net_bridge_mcast *brmctx,
421					      struct net_bridge_port_group *pg,
422					      union net_bridge_eht_addr *src_addr,
423					      union net_bridge_eht_addr *h_addr,
424					      int filter_mode,
425					      bool allow_zero_src)
426{
427	struct net_bridge_group_eht_set_entry *set_h;
428	struct net_bridge_group_eht_host *eht_host;
429	struct net_bridge *br = pg->key.port->br;
430	struct net_bridge_group_eht_set *eht_set;
431	union net_bridge_eht_addr zero_addr;
432
433	memset(&zero_addr, 0, sizeof(zero_addr));
434	if (!allow_zero_src && !memcmp(src_addr, &zero_addr, sizeof(zero_addr)))
435		return;
436
437	eht_set = __eht_lookup_create_set(pg, src_addr);
438	if (!eht_set)
439		return;
440
441	eht_host = __eht_lookup_create_host(pg, h_addr, filter_mode);
442	if (!eht_host)
443		goto fail_host;
444
445	set_h = __eht_lookup_create_set_entry(br, eht_set, eht_host,
446					      allow_zero_src);
447	if (!set_h)
448		goto fail_set_entry;
449
450	mod_timer(&set_h->timer, jiffies + br_multicast_gmi(brmctx));
451	mod_timer(&eht_set->timer, jiffies + br_multicast_gmi(brmctx));
452
453	return;
454
455fail_set_entry:
456	if (hlist_empty(&eht_host->set_entries))
457		__eht_destroy_host(eht_host);
458fail_host:
459	if (RB_EMPTY_ROOT(&eht_set->entry_tree))
460		br_multicast_del_eht_set(eht_set);
461}
462
463static bool br_multicast_del_eht_set_entry(struct net_bridge_port_group *pg,
464					   union net_bridge_eht_addr *src_addr,
465					   union net_bridge_eht_addr *h_addr)
466{
467	struct net_bridge_group_eht_set_entry *set_h;
468	struct net_bridge_group_eht_set *eht_set;
469	bool set_deleted = false;
470
471	eht_set = br_multicast_eht_set_lookup(pg, src_addr);
472	if (!eht_set)
473		goto out;
474
475	set_h = br_multicast_eht_set_entry_lookup(eht_set, h_addr);
476	if (!set_h)
477		goto out;
478
479	__eht_del_set_entry(set_h);
480
481	if (RB_EMPTY_ROOT(&eht_set->entry_tree)) {
482		br_multicast_del_eht_set(eht_set);
483		set_deleted = true;
484	}
485
486out:
487	return set_deleted;
488}
489
490static void br_multicast_del_eht_host(struct net_bridge_port_group *pg,
491				      union net_bridge_eht_addr *h_addr)
492{
493	struct net_bridge_group_eht_set_entry *set_h;
494	struct net_bridge_group_eht_host *eht_host;
495	struct hlist_node *tmp;
496
497	eht_host = br_multicast_eht_host_lookup(pg, h_addr);
498	if (!eht_host)
499		return;
500
501	hlist_for_each_entry_safe(set_h, tmp, &eht_host->set_entries, host_list)
502		br_multicast_del_eht_set_entry(set_h->eht_set->pg,
503					       &set_h->eht_set->src_addr,
504					       &set_h->h_addr);
505}
506
507/* create new set entries from reports */
508static void __eht_create_set_entries(const struct net_bridge_mcast *brmctx,
509				     struct net_bridge_port_group *pg,
510				     union net_bridge_eht_addr *h_addr,
511				     void *srcs,
512				     u32 nsrcs,
513				     size_t addr_size,
514				     int filter_mode)
515{
516	union net_bridge_eht_addr eht_src_addr;
517	u32 src_idx;
518
519	memset(&eht_src_addr, 0, sizeof(eht_src_addr));
520	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
521		memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
522		br_multicast_create_eht_set_entry(brmctx, pg, &eht_src_addr,
523						  h_addr, filter_mode,
524						  false);
525	}
526}
527
528/* delete existing set entries and their (S,G) entries if they were the last */
529static bool __eht_del_set_entries(struct net_bridge_port_group *pg,
530				  union net_bridge_eht_addr *h_addr,
531				  void *srcs,
532				  u32 nsrcs,
533				  size_t addr_size)
534{
535	union net_bridge_eht_addr eht_src_addr;
536	struct net_bridge_group_src *src_ent;
537	bool changed = false;
538	struct br_ip src_ip;
539	u32 src_idx;
540
541	memset(&eht_src_addr, 0, sizeof(eht_src_addr));
542	memset(&src_ip, 0, sizeof(src_ip));
543	src_ip.proto = pg->key.addr.proto;
544	for (src_idx = 0; src_idx < nsrcs; src_idx++) {
545		memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
546		if (!br_multicast_del_eht_set_entry(pg, &eht_src_addr, h_addr))
547			continue;
548		memcpy(&src_ip, srcs + (src_idx * addr_size), addr_size);
549		src_ent = br_multicast_find_group_src(pg, &src_ip);
550		if (!src_ent)
551			continue;
552		br_multicast_del_group_src(src_ent, true);
553		changed = true;
554	}
555
556	return changed;
557}
558
559static bool br_multicast_eht_allow(const struct net_bridge_mcast *brmctx,
560				   struct net_bridge_port_group *pg,
561				   union net_bridge_eht_addr *h_addr,
562				   void *srcs,
563				   u32 nsrcs,
564				   size_t addr_size)
565{
566	bool changed = false;
567
568	switch (br_multicast_eht_host_filter_mode(pg, h_addr)) {
569	case MCAST_INCLUDE:
570		__eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs,
571					 addr_size, MCAST_INCLUDE);
572		break;
573	case MCAST_EXCLUDE:
574		changed = __eht_del_set_entries(pg, h_addr, srcs, nsrcs,
575						addr_size);
576		break;
577	}
578
579	return changed;
580}
581
582static bool br_multicast_eht_block(const struct net_bridge_mcast *brmctx,
583				   struct net_bridge_port_group *pg,
584				   union net_bridge_eht_addr *h_addr,
585				   void *srcs,
586				   u32 nsrcs,
587				   size_t addr_size)
588{
589	bool changed = false;
590
591	switch (br_multicast_eht_host_filter_mode(pg, h_addr)) {
592	case MCAST_INCLUDE:
593		changed = __eht_del_set_entries(pg, h_addr, srcs, nsrcs,
594						addr_size);
595		break;
596	case MCAST_EXCLUDE:
597		__eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
598					 MCAST_EXCLUDE);
599		break;
600	}
601
602	return changed;
603}
604
605/* flush_entries is true when changing mode */
606static bool __eht_inc_exc(const struct net_bridge_mcast *brmctx,
607			  struct net_bridge_port_group *pg,
608			  union net_bridge_eht_addr *h_addr,
609			  void *srcs,
610			  u32 nsrcs,
611			  size_t addr_size,
612			  unsigned char filter_mode,
613			  bool to_report)
614{
615	bool changed = false, flush_entries = to_report;
616	union net_bridge_eht_addr eht_src_addr;
617
618	if (br_multicast_eht_host_filter_mode(pg, h_addr) != filter_mode)
619		flush_entries = true;
620
621	memset(&eht_src_addr, 0, sizeof(eht_src_addr));
622	/* if we're changing mode del host and its entries */
623	if (flush_entries)
624		br_multicast_del_eht_host(pg, h_addr);
625	__eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
626				 filter_mode);
627	/* we can be missing sets only if we've deleted some entries */
628	if (flush_entries) {
629		struct net_bridge_group_eht_set *eht_set;
630		struct net_bridge_group_src *src_ent;
631		struct hlist_node *tmp;
632
633		hlist_for_each_entry_safe(src_ent, tmp, &pg->src_list, node) {
634			br_multicast_ip_src_to_eht_addr(&src_ent->addr,
635							&eht_src_addr);
636			if (!br_multicast_eht_set_lookup(pg, &eht_src_addr)) {
637				br_multicast_del_group_src(src_ent, true);
638				changed = true;
639				continue;
640			}
641			/* this is an optimization for TO_INCLUDE where we lower
642			 * the set's timeout to LMQT to catch timeout hosts:
643			 * - host A (timing out): set entries X, Y
644			 * - host B: set entry Z (new from current TO_INCLUDE)
645			 *           sends BLOCK Z after LMQT but host A's EHT
646			 *           entries still exist (unless lowered to LMQT
647			 *           so they can timeout with the S,Gs)
648			 * => we wait another LMQT, when we can just delete the
649			 *    group immediately
650			 */
651			if (!(src_ent->flags & BR_SGRP_F_SEND) ||
652			    filter_mode != MCAST_INCLUDE ||
653			    !to_report)
654				continue;
655			eht_set = br_multicast_eht_set_lookup(pg,
656							      &eht_src_addr);
657			if (!eht_set)
658				continue;
659			mod_timer(&eht_set->timer, jiffies + br_multicast_lmqt(brmctx));
660		}
661	}
662
663	return changed;
664}
665
666static bool br_multicast_eht_inc(const struct net_bridge_mcast *brmctx,
667				 struct net_bridge_port_group *pg,
668				 union net_bridge_eht_addr *h_addr,
669				 void *srcs,
670				 u32 nsrcs,
671				 size_t addr_size,
672				 bool to_report)
673{
674	bool changed;
675
676	changed = __eht_inc_exc(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
677				MCAST_INCLUDE, to_report);
678	br_eht_convert_host_filter_mode(brmctx, pg, h_addr, MCAST_INCLUDE);
679
680	return changed;
681}
682
683static bool br_multicast_eht_exc(const struct net_bridge_mcast *brmctx,
684				 struct net_bridge_port_group *pg,
685				 union net_bridge_eht_addr *h_addr,
686				 void *srcs,
687				 u32 nsrcs,
688				 size_t addr_size,
689				 bool to_report)
690{
691	bool changed;
692
693	changed = __eht_inc_exc(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
694				MCAST_EXCLUDE, to_report);
695	br_eht_convert_host_filter_mode(brmctx, pg, h_addr, MCAST_EXCLUDE);
696
697	return changed;
698}
699
700static bool __eht_ip4_handle(const struct net_bridge_mcast *brmctx,
701			     struct net_bridge_port_group *pg,
702			     union net_bridge_eht_addr *h_addr,
703			     void *srcs,
704			     u32 nsrcs,
705			     int grec_type)
706{
707	bool changed = false, to_report = false;
708
709	switch (grec_type) {
710	case IGMPV3_ALLOW_NEW_SOURCES:
711		br_multicast_eht_allow(brmctx, pg, h_addr, srcs, nsrcs,
712				       sizeof(__be32));
713		break;
714	case IGMPV3_BLOCK_OLD_SOURCES:
715		changed = br_multicast_eht_block(brmctx, pg, h_addr, srcs, nsrcs,
716						 sizeof(__be32));
717		break;
718	case IGMPV3_CHANGE_TO_INCLUDE:
719		to_report = true;
720		fallthrough;
721	case IGMPV3_MODE_IS_INCLUDE:
722		changed = br_multicast_eht_inc(brmctx, pg, h_addr, srcs, nsrcs,
723					       sizeof(__be32), to_report);
724		break;
725	case IGMPV3_CHANGE_TO_EXCLUDE:
726		to_report = true;
727		fallthrough;
728	case IGMPV3_MODE_IS_EXCLUDE:
729		changed = br_multicast_eht_exc(brmctx, pg, h_addr, srcs, nsrcs,
730					       sizeof(__be32), to_report);
731		break;
732	}
733
734	return changed;
735}
736
737#if IS_ENABLED(CONFIG_IPV6)
738static bool __eht_ip6_handle(const struct net_bridge_mcast *brmctx,
739			     struct net_bridge_port_group *pg,
740			     union net_bridge_eht_addr *h_addr,
741			     void *srcs,
742			     u32 nsrcs,
743			     int grec_type)
744{
745	bool changed = false, to_report = false;
746
747	switch (grec_type) {
748	case MLD2_ALLOW_NEW_SOURCES:
749		br_multicast_eht_allow(brmctx, pg, h_addr, srcs, nsrcs,
750				       sizeof(struct in6_addr));
751		break;
752	case MLD2_BLOCK_OLD_SOURCES:
753		changed = br_multicast_eht_block(brmctx, pg, h_addr, srcs, nsrcs,
754						 sizeof(struct in6_addr));
755		break;
756	case MLD2_CHANGE_TO_INCLUDE:
757		to_report = true;
758		fallthrough;
759	case MLD2_MODE_IS_INCLUDE:
760		changed = br_multicast_eht_inc(brmctx, pg, h_addr, srcs, nsrcs,
761					       sizeof(struct in6_addr),
762					       to_report);
763		break;
764	case MLD2_CHANGE_TO_EXCLUDE:
765		to_report = true;
766		fallthrough;
767	case MLD2_MODE_IS_EXCLUDE:
768		changed = br_multicast_eht_exc(brmctx, pg, h_addr, srcs, nsrcs,
769					       sizeof(struct in6_addr),
770					       to_report);
771		break;
772	}
773
774	return changed;
775}
776#endif
777
778/* true means an entry was deleted */
779bool br_multicast_eht_handle(const struct net_bridge_mcast *brmctx,
780			     struct net_bridge_port_group *pg,
781			     void *h_addr,
782			     void *srcs,
783			     u32 nsrcs,
784			     size_t addr_size,
785			     int grec_type)
786{
787	bool eht_enabled = !!(pg->key.port->flags & BR_MULTICAST_FAST_LEAVE);
788	union net_bridge_eht_addr eht_host_addr;
789	bool changed = false;
790
791	if (!eht_enabled)
792		goto out;
793
794	memset(&eht_host_addr, 0, sizeof(eht_host_addr));
795	memcpy(&eht_host_addr, h_addr, addr_size);
796	if (addr_size == sizeof(__be32))
797		changed = __eht_ip4_handle(brmctx, pg, &eht_host_addr, srcs,
798					   nsrcs, grec_type);
799#if IS_ENABLED(CONFIG_IPV6)
800	else
801		changed = __eht_ip6_handle(brmctx, pg, &eht_host_addr, srcs,
802					   nsrcs, grec_type);
803#endif
804
805out:
806	return changed;
807}
808
809int br_multicast_eht_set_hosts_limit(struct net_bridge_port *p,
810				     u32 eht_hosts_limit)
811{
812	struct net_bridge *br = p->br;
813
814	if (!eht_hosts_limit)
815		return -EINVAL;
816
817	spin_lock_bh(&br->multicast_lock);
818	p->multicast_eht_hosts_limit = eht_hosts_limit;
819	spin_unlock_bh(&br->multicast_lock);
820
821	return 0;
822}
~/ Read-only file preview.