blob: 445768c8495f1d0f83bc273d73c6112b62440466 [file] [log] [blame]
Nikolay Aleksandrov5b163282021-01-20 16:51:55 +02001// 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
Nikolay Aleksandrovbaa74d32021-01-20 16:51:56 +020033static 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(struct net_bridge_port_group *pg,
37 union net_bridge_eht_addr *src_addr,
38 union net_bridge_eht_addr *h_addr,
39 int filter_mode,
40 bool allow_zero_src);
41
Nikolay Aleksandrov5b163282021-01-20 16:51:55 +020042static struct net_bridge_group_eht_host *
43br_multicast_eht_host_lookup(struct net_bridge_port_group *pg,
44 union net_bridge_eht_addr *h_addr)
45{
46 struct rb_node *node = pg->eht_host_tree.rb_node;
47
48 while (node) {
49 struct net_bridge_group_eht_host *this;
50 int result;
51
52 this = rb_entry(node, struct net_bridge_group_eht_host,
53 rb_node);
54 result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr));
55 if (result < 0)
56 node = node->rb_left;
57 else if (result > 0)
58 node = node->rb_right;
59 else
60 return this;
61 }
62
63 return NULL;
64}
65
66static int br_multicast_eht_host_filter_mode(struct net_bridge_port_group *pg,
67 union net_bridge_eht_addr *h_addr)
68{
69 struct net_bridge_group_eht_host *eht_host;
70
71 eht_host = br_multicast_eht_host_lookup(pg, h_addr);
72 if (!eht_host)
73 return MCAST_INCLUDE;
74
75 return eht_host->filter_mode;
76}
77
Nikolay Aleksandrovbaa74d32021-01-20 16:51:56 +020078static struct net_bridge_group_eht_set_entry *
79br_multicast_eht_set_entry_lookup(struct net_bridge_group_eht_set *eht_set,
80 union net_bridge_eht_addr *h_addr)
81{
82 struct rb_node *node = eht_set->entry_tree.rb_node;
83
84 while (node) {
85 struct net_bridge_group_eht_set_entry *this;
86 int result;
87
Nikolay Aleksandrov3e841ba2021-01-25 10:20:40 +020088 this = rb_entry(node, struct net_bridge_group_eht_set_entry,
89 rb_node);
90 result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr));
91 if (result < 0)
92 node = node->rb_left;
93 else if (result > 0)
94 node = node->rb_right;
95 else
96 return this;
Nikolay Aleksandrovbaa74d32021-01-20 16:51:56 +020097 }
98
99 return NULL;
100}
101
102static struct net_bridge_group_eht_set *
103br_multicast_eht_set_lookup(struct net_bridge_port_group *pg,
104 union net_bridge_eht_addr *src_addr)
105{
106 struct rb_node *node = pg->eht_set_tree.rb_node;
107
108 while (node) {
109 struct net_bridge_group_eht_set *this;
110 int result;
111
112 this = rb_entry(node, struct net_bridge_group_eht_set,
113 rb_node);
114 result = memcmp(src_addr, &this->src_addr, sizeof(*src_addr));
115 if (result < 0)
116 node = node->rb_left;
117 else if (result > 0)
118 node = node->rb_right;
119 else
120 return this;
121 }
122
123 return NULL;
124}
125
Nikolay Aleksandrov5b163282021-01-20 16:51:55 +0200126static void __eht_destroy_host(struct net_bridge_group_eht_host *eht_host)
127{
128 WARN_ON(!hlist_empty(&eht_host->set_entries));
129
Nikolay Aleksandrov89268b02021-01-26 11:35:32 +0200130 br_multicast_eht_hosts_dec(eht_host->pg);
131
Nikolay Aleksandrov5b163282021-01-20 16:51:55 +0200132 rb_erase(&eht_host->rb_node, &eht_host->pg->eht_host_tree);
133 RB_CLEAR_NODE(&eht_host->rb_node);
134 kfree(eht_host);
135}
136
Nikolay Aleksandrovbaa74d32021-01-20 16:51:56 +0200137static void br_multicast_destroy_eht_set_entry(struct net_bridge_mcast_gc *gc)
138{
139 struct net_bridge_group_eht_set_entry *set_h;
140
141 set_h = container_of(gc, struct net_bridge_group_eht_set_entry, mcast_gc);
142 WARN_ON(!RB_EMPTY_NODE(&set_h->rb_node));
143
144 del_timer_sync(&set_h->timer);
145 kfree(set_h);
146}
147
148static void br_multicast_destroy_eht_set(struct net_bridge_mcast_gc *gc)
149{
150 struct net_bridge_group_eht_set *eht_set;
151
152 eht_set = container_of(gc, struct net_bridge_group_eht_set, mcast_gc);
153 WARN_ON(!RB_EMPTY_NODE(&eht_set->rb_node));
154 WARN_ON(!RB_EMPTY_ROOT(&eht_set->entry_tree));
155
156 del_timer_sync(&eht_set->timer);
157 kfree(eht_set);
158}
159
160static void __eht_del_set_entry(struct net_bridge_group_eht_set_entry *set_h)
161{
162 struct net_bridge_group_eht_host *eht_host = set_h->h_parent;
163 union net_bridge_eht_addr zero_addr;
164
165 rb_erase(&set_h->rb_node, &set_h->eht_set->entry_tree);
166 RB_CLEAR_NODE(&set_h->rb_node);
167 hlist_del_init(&set_h->host_list);
168 memset(&zero_addr, 0, sizeof(zero_addr));
169 if (memcmp(&set_h->h_addr, &zero_addr, sizeof(zero_addr)))
170 eht_host->num_entries--;
171 hlist_add_head(&set_h->mcast_gc.gc_node, &set_h->br->mcast_gc_list);
172 queue_work(system_long_wq, &set_h->br->mcast_gc_work);
173
174 if (hlist_empty(&eht_host->set_entries))
175 __eht_destroy_host(eht_host);
176}
177
178static void br_multicast_del_eht_set(struct net_bridge_group_eht_set *eht_set)
179{
180 struct net_bridge_group_eht_set_entry *set_h;
181 struct rb_node *node;
182
183 while ((node = rb_first(&eht_set->entry_tree))) {
184 set_h = rb_entry(node, struct net_bridge_group_eht_set_entry,
185 rb_node);
186 __eht_del_set_entry(set_h);
187 }
188
189 rb_erase(&eht_set->rb_node, &eht_set->pg->eht_set_tree);
190 RB_CLEAR_NODE(&eht_set->rb_node);
191 hlist_add_head(&eht_set->mcast_gc.gc_node, &eht_set->br->mcast_gc_list);
192 queue_work(system_long_wq, &eht_set->br->mcast_gc_work);
193}
194
195void br_multicast_eht_clean_sets(struct net_bridge_port_group *pg)
196{
197 struct net_bridge_group_eht_set *eht_set;
198 struct rb_node *node;
199
200 while ((node = rb_first(&pg->eht_set_tree))) {
201 eht_set = rb_entry(node, struct net_bridge_group_eht_set,
202 rb_node);
203 br_multicast_del_eht_set(eht_set);
204 }
205}
206
207static void br_multicast_eht_set_entry_expired(struct timer_list *t)
208{
209 struct net_bridge_group_eht_set_entry *set_h = from_timer(set_h, t, timer);
210 struct net_bridge *br = set_h->br;
211
212 spin_lock(&br->multicast_lock);
213 if (RB_EMPTY_NODE(&set_h->rb_node) || timer_pending(&set_h->timer))
214 goto out;
215
216 br_multicast_del_eht_set_entry(set_h->eht_set->pg,
217 &set_h->eht_set->src_addr,
218 &set_h->h_addr);
219out:
220 spin_unlock(&br->multicast_lock);
221}
222
223static void br_multicast_eht_set_expired(struct timer_list *t)
224{
225 struct net_bridge_group_eht_set *eht_set = from_timer(eht_set, t,
226 timer);
227 struct net_bridge *br = eht_set->br;
228
229 spin_lock(&br->multicast_lock);
230 if (RB_EMPTY_NODE(&eht_set->rb_node) || timer_pending(&eht_set->timer))
231 goto out;
232
233 br_multicast_del_eht_set(eht_set);
234out:
235 spin_unlock(&br->multicast_lock);
236}
237
Nikolay Aleksandrov5b163282021-01-20 16:51:55 +0200238static struct net_bridge_group_eht_host *
239__eht_lookup_create_host(struct net_bridge_port_group *pg,
240 union net_bridge_eht_addr *h_addr,
241 unsigned char filter_mode)
242{
243 struct rb_node **link = &pg->eht_host_tree.rb_node, *parent = NULL;
244 struct net_bridge_group_eht_host *eht_host;
245
246 while (*link) {
247 struct net_bridge_group_eht_host *this;
248 int result;
249
250 this = rb_entry(*link, struct net_bridge_group_eht_host,
251 rb_node);
252 result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr));
253 parent = *link;
254 if (result < 0)
255 link = &((*link)->rb_left);
256 else if (result > 0)
257 link = &((*link)->rb_right);
258 else
259 return this;
260 }
261
Nikolay Aleksandrov89268b02021-01-26 11:35:32 +0200262 if (br_multicast_eht_hosts_over_limit(pg))
263 return NULL;
264
Nikolay Aleksandrov5b163282021-01-20 16:51:55 +0200265 eht_host = kzalloc(sizeof(*eht_host), GFP_ATOMIC);
266 if (!eht_host)
267 return NULL;
268
269 memcpy(&eht_host->h_addr, h_addr, sizeof(*h_addr));
270 INIT_HLIST_HEAD(&eht_host->set_entries);
271 eht_host->pg = pg;
272 eht_host->filter_mode = filter_mode;
273
274 rb_link_node(&eht_host->rb_node, parent, link);
275 rb_insert_color(&eht_host->rb_node, &pg->eht_host_tree);
276
Nikolay Aleksandrov89268b02021-01-26 11:35:32 +0200277 br_multicast_eht_hosts_inc(pg);
278
Nikolay Aleksandrov5b163282021-01-20 16:51:55 +0200279 return eht_host;
280}
Nikolay Aleksandrovbaa74d32021-01-20 16:51:56 +0200281
282static struct net_bridge_group_eht_set_entry *
283__eht_lookup_create_set_entry(struct net_bridge *br,
284 struct net_bridge_group_eht_set *eht_set,
285 struct net_bridge_group_eht_host *eht_host,
286 bool allow_zero_src)
287{
288 struct rb_node **link = &eht_set->entry_tree.rb_node, *parent = NULL;
289 struct net_bridge_group_eht_set_entry *set_h;
290
291 while (*link) {
292 struct net_bridge_group_eht_set_entry *this;
293 int result;
294
295 this = rb_entry(*link, struct net_bridge_group_eht_set_entry,
296 rb_node);
297 result = memcmp(&eht_host->h_addr, &this->h_addr,
298 sizeof(union net_bridge_eht_addr));
299 parent = *link;
300 if (result < 0)
301 link = &((*link)->rb_left);
302 else if (result > 0)
303 link = &((*link)->rb_right);
304 else
305 return this;
306 }
307
308 /* always allow auto-created zero entry */
309 if (!allow_zero_src && eht_host->num_entries >= PG_SRC_ENT_LIMIT)
310 return NULL;
311
312 set_h = kzalloc(sizeof(*set_h), GFP_ATOMIC);
313 if (!set_h)
314 return NULL;
315
316 memcpy(&set_h->h_addr, &eht_host->h_addr,
317 sizeof(union net_bridge_eht_addr));
318 set_h->mcast_gc.destroy = br_multicast_destroy_eht_set_entry;
319 set_h->eht_set = eht_set;
320 set_h->h_parent = eht_host;
321 set_h->br = br;
322 timer_setup(&set_h->timer, br_multicast_eht_set_entry_expired, 0);
323
324 hlist_add_head(&set_h->host_list, &eht_host->set_entries);
325 rb_link_node(&set_h->rb_node, parent, link);
326 rb_insert_color(&set_h->rb_node, &eht_set->entry_tree);
327 /* we must not count the auto-created zero entry otherwise we won't be
328 * able to track the full list of PG_SRC_ENT_LIMIT entries
329 */
330 if (!allow_zero_src)
331 eht_host->num_entries++;
332
333 return set_h;
334}
335
336static struct net_bridge_group_eht_set *
337__eht_lookup_create_set(struct net_bridge_port_group *pg,
338 union net_bridge_eht_addr *src_addr)
339{
340 struct rb_node **link = &pg->eht_set_tree.rb_node, *parent = NULL;
341 struct net_bridge_group_eht_set *eht_set;
342
343 while (*link) {
344 struct net_bridge_group_eht_set *this;
345 int result;
346
347 this = rb_entry(*link, struct net_bridge_group_eht_set,
348 rb_node);
349 result = memcmp(src_addr, &this->src_addr, sizeof(*src_addr));
350 parent = *link;
351 if (result < 0)
352 link = &((*link)->rb_left);
353 else if (result > 0)
354 link = &((*link)->rb_right);
355 else
356 return this;
357 }
358
359 eht_set = kzalloc(sizeof(*eht_set), GFP_ATOMIC);
360 if (!eht_set)
361 return NULL;
362
363 memcpy(&eht_set->src_addr, src_addr, sizeof(*src_addr));
364 eht_set->mcast_gc.destroy = br_multicast_destroy_eht_set;
365 eht_set->pg = pg;
366 eht_set->br = pg->key.port->br;
367 eht_set->entry_tree = RB_ROOT;
368 timer_setup(&eht_set->timer, br_multicast_eht_set_expired, 0);
369
370 rb_link_node(&eht_set->rb_node, parent, link);
371 rb_insert_color(&eht_set->rb_node, &pg->eht_set_tree);
372
373 return eht_set;
374}
375
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200376static void br_multicast_ip_src_to_eht_addr(const struct br_ip *src,
377 union net_bridge_eht_addr *dest)
378{
379 switch (src->proto) {
380 case htons(ETH_P_IP):
381 dest->ip4 = src->src.ip4;
382 break;
383#if IS_ENABLED(CONFIG_IPV6)
384 case htons(ETH_P_IPV6):
385 memcpy(&dest->ip6, &src->src.ip6, sizeof(struct in6_addr));
386 break;
387#endif
388 }
389}
390
Nikolay Aleksandrovc9739012021-01-20 16:52:01 +0200391static void br_eht_convert_host_filter_mode(struct net_bridge_port_group *pg,
392 union net_bridge_eht_addr *h_addr,
393 int filter_mode)
394{
395 struct net_bridge_group_eht_host *eht_host;
396 union net_bridge_eht_addr zero_addr;
397
398 eht_host = br_multicast_eht_host_lookup(pg, h_addr);
399 if (eht_host)
400 eht_host->filter_mode = filter_mode;
401
402 memset(&zero_addr, 0, sizeof(zero_addr));
403 switch (filter_mode) {
404 case MCAST_INCLUDE:
405 br_multicast_del_eht_set_entry(pg, &zero_addr, h_addr);
406 break;
407 case MCAST_EXCLUDE:
408 br_multicast_create_eht_set_entry(pg, &zero_addr, h_addr,
409 MCAST_EXCLUDE,
410 true);
411 break;
412 }
413}
414
Nikolay Aleksandrovbaa74d32021-01-20 16:51:56 +0200415static void br_multicast_create_eht_set_entry(struct net_bridge_port_group *pg,
416 union net_bridge_eht_addr *src_addr,
417 union net_bridge_eht_addr *h_addr,
418 int filter_mode,
419 bool allow_zero_src)
420{
421 struct net_bridge_group_eht_set_entry *set_h;
422 struct net_bridge_group_eht_host *eht_host;
423 struct net_bridge *br = pg->key.port->br;
424 struct net_bridge_group_eht_set *eht_set;
425 union net_bridge_eht_addr zero_addr;
426
427 memset(&zero_addr, 0, sizeof(zero_addr));
428 if (!allow_zero_src && !memcmp(src_addr, &zero_addr, sizeof(zero_addr)))
429 return;
430
431 eht_set = __eht_lookup_create_set(pg, src_addr);
432 if (!eht_set)
433 return;
434
435 eht_host = __eht_lookup_create_host(pg, h_addr, filter_mode);
436 if (!eht_host)
437 goto fail_host;
438
439 set_h = __eht_lookup_create_set_entry(br, eht_set, eht_host,
440 allow_zero_src);
441 if (!set_h)
442 goto fail_set_entry;
443
444 mod_timer(&set_h->timer, jiffies + br_multicast_gmi(br));
445 mod_timer(&eht_set->timer, jiffies + br_multicast_gmi(br));
446
447 return;
448
449fail_set_entry:
450 if (hlist_empty(&eht_host->set_entries))
451 __eht_destroy_host(eht_host);
452fail_host:
453 if (RB_EMPTY_ROOT(&eht_set->entry_tree))
454 br_multicast_del_eht_set(eht_set);
455}
456
457static bool br_multicast_del_eht_set_entry(struct net_bridge_port_group *pg,
458 union net_bridge_eht_addr *src_addr,
459 union net_bridge_eht_addr *h_addr)
460{
461 struct net_bridge_group_eht_set_entry *set_h;
462 struct net_bridge_group_eht_set *eht_set;
463 bool set_deleted = false;
464
465 eht_set = br_multicast_eht_set_lookup(pg, src_addr);
466 if (!eht_set)
467 goto out;
468
469 set_h = br_multicast_eht_set_entry_lookup(eht_set, h_addr);
470 if (!set_h)
471 goto out;
472
473 __eht_del_set_entry(set_h);
474
475 if (RB_EMPTY_ROOT(&eht_set->entry_tree)) {
476 br_multicast_del_eht_set(eht_set);
477 set_deleted = true;
478 }
479
480out:
481 return set_deleted;
482}
Nikolay Aleksandrovdba6b0a2021-01-20 16:51:57 +0200483
484static void br_multicast_del_eht_host(struct net_bridge_port_group *pg,
485 union net_bridge_eht_addr *h_addr)
486{
487 struct net_bridge_group_eht_set_entry *set_h;
488 struct net_bridge_group_eht_host *eht_host;
489 struct hlist_node *tmp;
490
491 eht_host = br_multicast_eht_host_lookup(pg, h_addr);
492 if (!eht_host)
493 return;
494
495 hlist_for_each_entry_safe(set_h, tmp, &eht_host->set_entries, host_list)
496 br_multicast_del_eht_set_entry(set_h->eht_set->pg,
497 &set_h->eht_set->src_addr,
498 &set_h->h_addr);
499}
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200500
501static void __eht_allow_incl(struct net_bridge_port_group *pg,
502 union net_bridge_eht_addr *h_addr,
503 void *srcs,
504 u32 nsrcs,
505 size_t addr_size)
506{
507 union net_bridge_eht_addr eht_src_addr;
508 u32 src_idx;
509
510 memset(&eht_src_addr, 0, sizeof(eht_src_addr));
511 for (src_idx = 0; src_idx < nsrcs; src_idx++) {
512 memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
513 br_multicast_create_eht_set_entry(pg, &eht_src_addr, h_addr,
514 MCAST_INCLUDE,
515 false);
516 }
517}
518
519static bool __eht_allow_excl(struct net_bridge_port_group *pg,
520 union net_bridge_eht_addr *h_addr,
521 void *srcs,
522 u32 nsrcs,
523 size_t addr_size)
524{
525 bool changed = false, host_excl = false;
526 union net_bridge_eht_addr eht_src_addr;
527 struct net_bridge_group_src *src_ent;
528 struct br_ip src_ip;
529 u32 src_idx;
530
531 host_excl = !!(br_multicast_eht_host_filter_mode(pg, h_addr) == MCAST_EXCLUDE);
532 memset(&eht_src_addr, 0, sizeof(eht_src_addr));
533 for (src_idx = 0; src_idx < nsrcs; src_idx++) {
534 memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
535 if (!host_excl) {
536 br_multicast_create_eht_set_entry(pg, &eht_src_addr, h_addr,
537 MCAST_INCLUDE,
538 false);
539 } else {
540 if (!br_multicast_del_eht_set_entry(pg, &eht_src_addr,
541 h_addr))
542 continue;
543 memcpy(&src_ip, srcs + (src_idx * addr_size), addr_size);
544 src_ent = br_multicast_find_group_src(pg, &src_ip);
545 if (!src_ent)
546 continue;
Nikolay Aleksandrovd5a10222021-01-20 16:52:03 +0200547 br_multicast_del_group_src(src_ent, true);
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200548 changed = true;
549 }
550 }
551
552 return changed;
553}
554
555static bool br_multicast_eht_allow(struct net_bridge_port_group *pg,
556 union net_bridge_eht_addr *h_addr,
557 void *srcs,
558 u32 nsrcs,
559 size_t addr_size)
560{
561 bool changed = false;
562
563 switch (br_multicast_eht_host_filter_mode(pg, h_addr)) {
564 case MCAST_INCLUDE:
565 __eht_allow_incl(pg, h_addr, srcs, nsrcs, addr_size);
566 break;
567 case MCAST_EXCLUDE:
568 changed = __eht_allow_excl(pg, h_addr, srcs, nsrcs, addr_size);
569 break;
570 }
571
572 return changed;
573}
574
575static bool __eht_block_incl(struct net_bridge_port_group *pg,
576 union net_bridge_eht_addr *h_addr,
577 void *srcs,
578 u32 nsrcs,
579 size_t addr_size)
580{
581 union net_bridge_eht_addr eht_src_addr;
582 struct net_bridge_group_src *src_ent;
583 bool changed = false;
584 struct br_ip src_ip;
585 u32 src_idx;
586
587 memset(&eht_src_addr, 0, sizeof(eht_src_addr));
588 memset(&src_ip, 0, sizeof(src_ip));
589 src_ip.proto = pg->key.addr.proto;
590 for (src_idx = 0; src_idx < nsrcs; src_idx++) {
591 memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
592 if (!br_multicast_del_eht_set_entry(pg, &eht_src_addr, h_addr))
593 continue;
594 memcpy(&src_ip, srcs + (src_idx * addr_size), addr_size);
595 src_ent = br_multicast_find_group_src(pg, &src_ip);
596 if (!src_ent)
597 continue;
Nikolay Aleksandrovd5a10222021-01-20 16:52:03 +0200598 br_multicast_del_group_src(src_ent, true);
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200599 changed = true;
600 }
601
602 return changed;
603}
604
605static bool __eht_block_excl(struct net_bridge_port_group *pg,
606 union net_bridge_eht_addr *h_addr,
607 void *srcs,
608 u32 nsrcs,
609 size_t addr_size)
610{
611 bool changed = false, host_excl = false;
612 union net_bridge_eht_addr eht_src_addr;
613 struct net_bridge_group_src *src_ent;
614 struct br_ip src_ip;
615 u32 src_idx;
616
617 host_excl = !!(br_multicast_eht_host_filter_mode(pg, h_addr) == MCAST_EXCLUDE);
618 memset(&eht_src_addr, 0, sizeof(eht_src_addr));
619 memset(&src_ip, 0, sizeof(src_ip));
620 src_ip.proto = pg->key.addr.proto;
621 for (src_idx = 0; src_idx < nsrcs; src_idx++) {
622 memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
623 if (host_excl) {
624 br_multicast_create_eht_set_entry(pg, &eht_src_addr, h_addr,
625 MCAST_EXCLUDE,
626 false);
627 } else {
628 if (!br_multicast_del_eht_set_entry(pg, &eht_src_addr,
629 h_addr))
630 continue;
631 memcpy(&src_ip, srcs + (src_idx * addr_size), addr_size);
632 src_ent = br_multicast_find_group_src(pg, &src_ip);
633 if (!src_ent)
634 continue;
Nikolay Aleksandrovd5a10222021-01-20 16:52:03 +0200635 br_multicast_del_group_src(src_ent, true);
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200636 changed = true;
637 }
638 }
639
640 return changed;
641}
642
643static bool br_multicast_eht_block(struct net_bridge_port_group *pg,
644 union net_bridge_eht_addr *h_addr,
645 void *srcs,
646 u32 nsrcs,
647 size_t addr_size)
648{
649 bool changed = false;
650
651 switch (br_multicast_eht_host_filter_mode(pg, h_addr)) {
652 case MCAST_INCLUDE:
653 changed = __eht_block_incl(pg, h_addr, srcs, nsrcs, addr_size);
654 break;
655 case MCAST_EXCLUDE:
656 changed = __eht_block_excl(pg, h_addr, srcs, nsrcs, addr_size);
657 break;
658 }
659
660 return changed;
661}
662
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200663/* flush_entries is true when changing mode */
664static bool __eht_inc_exc(struct net_bridge_port_group *pg,
665 union net_bridge_eht_addr *h_addr,
666 void *srcs,
667 u32 nsrcs,
668 size_t addr_size,
669 unsigned char filter_mode,
670 bool to_report)
671{
672 bool changed = false, flush_entries = to_report;
673 union net_bridge_eht_addr eht_src_addr;
674 u32 src_idx;
675
676 if (br_multicast_eht_host_filter_mode(pg, h_addr) != filter_mode)
677 flush_entries = true;
678
679 memset(&eht_src_addr, 0, sizeof(eht_src_addr));
680 /* if we're changing mode del host and its entries */
681 if (flush_entries)
682 br_multicast_del_eht_host(pg, h_addr);
683 for (src_idx = 0; src_idx < nsrcs; src_idx++) {
684 memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
685 br_multicast_create_eht_set_entry(pg, &eht_src_addr, h_addr,
686 filter_mode, false);
687 }
688 /* we can be missing sets only if we've deleted some entries */
689 if (flush_entries) {
Nikolay Aleksandrovb66bf552021-01-20 16:52:00 +0200690 struct net_bridge *br = pg->key.port->br;
691 struct net_bridge_group_eht_set *eht_set;
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200692 struct net_bridge_group_src *src_ent;
693 struct hlist_node *tmp;
694
695 hlist_for_each_entry_safe(src_ent, tmp, &pg->src_list, node) {
696 br_multicast_ip_src_to_eht_addr(&src_ent->addr,
697 &eht_src_addr);
698 if (!br_multicast_eht_set_lookup(pg, &eht_src_addr)) {
Nikolay Aleksandrovd5a10222021-01-20 16:52:03 +0200699 br_multicast_del_group_src(src_ent, true);
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200700 changed = true;
701 continue;
702 }
Nikolay Aleksandrovb66bf552021-01-20 16:52:00 +0200703 /* this is an optimization for TO_INCLUDE where we lower
704 * the set's timeout to LMQT to catch timeout hosts:
705 * - host A (timing out): set entries X, Y
706 * - host B: set entry Z (new from current TO_INCLUDE)
707 * sends BLOCK Z after LMQT but host A's EHT
708 * entries still exist (unless lowered to LMQT
709 * so they can timeout with the S,Gs)
710 * => we wait another LMQT, when we can just delete the
711 * group immediately
712 */
713 if (!(src_ent->flags & BR_SGRP_F_SEND) ||
714 filter_mode != MCAST_INCLUDE ||
715 !to_report)
716 continue;
717 eht_set = br_multicast_eht_set_lookup(pg,
718 &eht_src_addr);
719 if (!eht_set)
720 continue;
721 mod_timer(&eht_set->timer, jiffies + br_multicast_lmqt(br));
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200722 }
723 }
724
725 return changed;
726}
727
728static bool br_multicast_eht_inc(struct net_bridge_port_group *pg,
729 union net_bridge_eht_addr *h_addr,
730 void *srcs,
731 u32 nsrcs,
732 size_t addr_size,
733 bool to_report)
734{
Nikolay Aleksandrovc9739012021-01-20 16:52:01 +0200735 bool changed;
736
737 changed = __eht_inc_exc(pg, h_addr, srcs, nsrcs, addr_size,
738 MCAST_INCLUDE, to_report);
739 br_eht_convert_host_filter_mode(pg, h_addr, MCAST_INCLUDE);
740
741 return changed;
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200742}
743
744static bool br_multicast_eht_exc(struct net_bridge_port_group *pg,
745 union net_bridge_eht_addr *h_addr,
746 void *srcs,
747 u32 nsrcs,
748 size_t addr_size,
749 bool to_report)
750{
Nikolay Aleksandrovc9739012021-01-20 16:52:01 +0200751 bool changed;
752
753 changed = __eht_inc_exc(pg, h_addr, srcs, nsrcs, addr_size,
754 MCAST_EXCLUDE, to_report);
755 br_eht_convert_host_filter_mode(pg, h_addr, MCAST_EXCLUDE);
756
757 return changed;
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200758}
759
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200760static bool __eht_ip4_handle(struct net_bridge_port_group *pg,
761 union net_bridge_eht_addr *h_addr,
762 void *srcs,
763 u32 nsrcs,
764 int grec_type)
765{
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200766 bool changed = false, to_report = false;
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200767
768 switch (grec_type) {
769 case IGMPV3_ALLOW_NEW_SOURCES:
770 br_multicast_eht_allow(pg, h_addr, srcs, nsrcs, sizeof(__be32));
771 break;
772 case IGMPV3_BLOCK_OLD_SOURCES:
773 changed = br_multicast_eht_block(pg, h_addr, srcs, nsrcs,
774 sizeof(__be32));
775 break;
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200776 case IGMPV3_CHANGE_TO_INCLUDE:
777 to_report = true;
778 fallthrough;
779 case IGMPV3_MODE_IS_INCLUDE:
780 changed = br_multicast_eht_inc(pg, h_addr, srcs, nsrcs,
781 sizeof(__be32), to_report);
782 break;
783 case IGMPV3_CHANGE_TO_EXCLUDE:
784 to_report = true;
785 fallthrough;
786 case IGMPV3_MODE_IS_EXCLUDE:
787 changed = br_multicast_eht_exc(pg, h_addr, srcs, nsrcs,
788 sizeof(__be32), to_report);
789 break;
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200790 }
791
792 return changed;
793}
794
795#if IS_ENABLED(CONFIG_IPV6)
796static bool __eht_ip6_handle(struct net_bridge_port_group *pg,
797 union net_bridge_eht_addr *h_addr,
798 void *srcs,
799 u32 nsrcs,
800 int grec_type)
801{
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200802 bool changed = false, to_report = false;
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200803
804 switch (grec_type) {
805 case MLD2_ALLOW_NEW_SOURCES:
806 br_multicast_eht_allow(pg, h_addr, srcs, nsrcs,
807 sizeof(struct in6_addr));
808 break;
809 case MLD2_BLOCK_OLD_SOURCES:
810 changed = br_multicast_eht_block(pg, h_addr, srcs, nsrcs,
811 sizeof(struct in6_addr));
812 break;
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200813 case MLD2_CHANGE_TO_INCLUDE:
814 to_report = true;
815 fallthrough;
816 case MLD2_MODE_IS_INCLUDE:
817 changed = br_multicast_eht_inc(pg, h_addr, srcs, nsrcs,
818 sizeof(struct in6_addr),
819 to_report);
820 break;
821 case MLD2_CHANGE_TO_EXCLUDE:
822 to_report = true;
823 fallthrough;
824 case MLD2_MODE_IS_EXCLUDE:
825 changed = br_multicast_eht_exc(pg, h_addr, srcs, nsrcs,
826 sizeof(struct in6_addr),
827 to_report);
828 break;
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200829 }
830
831 return changed;
832}
833#endif
834
835/* true means an entry was deleted */
836bool br_multicast_eht_handle(struct net_bridge_port_group *pg,
837 void *h_addr,
838 void *srcs,
839 u32 nsrcs,
840 size_t addr_size,
841 int grec_type)
842{
843 bool eht_enabled = !!(pg->key.port->flags & BR_MULTICAST_FAST_LEAVE);
844 union net_bridge_eht_addr eht_host_addr;
845 bool changed = false;
846
847 if (!eht_enabled)
848 goto out;
849
850 memset(&eht_host_addr, 0, sizeof(eht_host_addr));
851 memcpy(&eht_host_addr, h_addr, addr_size);
852 if (addr_size == sizeof(__be32))
853 changed = __eht_ip4_handle(pg, &eht_host_addr, srcs, nsrcs,
854 grec_type);
855#if IS_ENABLED(CONFIG_IPV6)
856 else
857 changed = __eht_ip6_handle(pg, &eht_host_addr, srcs, nsrcs,
858 grec_type);
859#endif
860
861out:
862 return changed;
863}