blob: f91c071d16089aa10010a93e6736974b3d853976 [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);
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +030036static void br_multicast_create_eht_set_entry(const struct net_bridge_mcast *brmctx,
37 struct net_bridge_port_group *pg,
Nikolay Aleksandrovbaa74d32021-01-20 16:51:56 +020038 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
Nikolay Aleksandrov5b163282021-01-20 16:51:55 +020043static 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
Nikolay Aleksandrovbaa74d32021-01-20 16:51:56 +020079static 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
Nikolay Aleksandrov3e841ba2021-01-25 10:20:40 +020089 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;
Nikolay Aleksandrovbaa74d32021-01-20 16:51:56 +020098 }
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
Nikolay Aleksandrov5b163282021-01-20 16:51:55 +0200127static void __eht_destroy_host(struct net_bridge_group_eht_host *eht_host)
128{
129 WARN_ON(!hlist_empty(&eht_host->set_entries));
130
Nikolay Aleksandrov89268b02021-01-26 11:35:32 +0200131 br_multicast_eht_hosts_dec(eht_host->pg);
132
Nikolay Aleksandrov5b163282021-01-20 16:51:55 +0200133 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
Nikolay Aleksandrovbaa74d32021-01-20 16:51:56 +0200138static 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 del_timer_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 del_timer_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 = from_timer(set_h, t, timer);
211 struct net_bridge *br = set_h->br;
212
213 spin_lock(&br->multicast_lock);
214 if (RB_EMPTY_NODE(&set_h->rb_node) || timer_pending(&set_h->timer))
215 goto out;
216
217 br_multicast_del_eht_set_entry(set_h->eht_set->pg,
218 &set_h->eht_set->src_addr,
219 &set_h->h_addr);
220out:
221 spin_unlock(&br->multicast_lock);
222}
223
224static void br_multicast_eht_set_expired(struct timer_list *t)
225{
226 struct net_bridge_group_eht_set *eht_set = from_timer(eht_set, t,
227 timer);
228 struct net_bridge *br = eht_set->br;
229
230 spin_lock(&br->multicast_lock);
231 if (RB_EMPTY_NODE(&eht_set->rb_node) || timer_pending(&eht_set->timer))
232 goto out;
233
234 br_multicast_del_eht_set(eht_set);
235out:
236 spin_unlock(&br->multicast_lock);
237}
238
Nikolay Aleksandrov5b163282021-01-20 16:51:55 +0200239static struct net_bridge_group_eht_host *
240__eht_lookup_create_host(struct net_bridge_port_group *pg,
241 union net_bridge_eht_addr *h_addr,
242 unsigned char filter_mode)
243{
244 struct rb_node **link = &pg->eht_host_tree.rb_node, *parent = NULL;
245 struct net_bridge_group_eht_host *eht_host;
246
247 while (*link) {
248 struct net_bridge_group_eht_host *this;
249 int result;
250
251 this = rb_entry(*link, struct net_bridge_group_eht_host,
252 rb_node);
253 result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr));
254 parent = *link;
255 if (result < 0)
256 link = &((*link)->rb_left);
257 else if (result > 0)
258 link = &((*link)->rb_right);
259 else
260 return this;
261 }
262
Nikolay Aleksandrov89268b02021-01-26 11:35:32 +0200263 if (br_multicast_eht_hosts_over_limit(pg))
264 return NULL;
265
Nikolay Aleksandrov5b163282021-01-20 16:51:55 +0200266 eht_host = kzalloc(sizeof(*eht_host), GFP_ATOMIC);
267 if (!eht_host)
268 return NULL;
269
270 memcpy(&eht_host->h_addr, h_addr, sizeof(*h_addr));
271 INIT_HLIST_HEAD(&eht_host->set_entries);
272 eht_host->pg = pg;
273 eht_host->filter_mode = filter_mode;
274
275 rb_link_node(&eht_host->rb_node, parent, link);
276 rb_insert_color(&eht_host->rb_node, &pg->eht_host_tree);
277
Nikolay Aleksandrov89268b02021-01-26 11:35:32 +0200278 br_multicast_eht_hosts_inc(pg);
279
Nikolay Aleksandrov5b163282021-01-20 16:51:55 +0200280 return eht_host;
281}
Nikolay Aleksandrovbaa74d32021-01-20 16:51:56 +0200282
283static struct net_bridge_group_eht_set_entry *
284__eht_lookup_create_set_entry(struct net_bridge *br,
285 struct net_bridge_group_eht_set *eht_set,
286 struct net_bridge_group_eht_host *eht_host,
287 bool allow_zero_src)
288{
289 struct rb_node **link = &eht_set->entry_tree.rb_node, *parent = NULL;
290 struct net_bridge_group_eht_set_entry *set_h;
291
292 while (*link) {
293 struct net_bridge_group_eht_set_entry *this;
294 int result;
295
296 this = rb_entry(*link, struct net_bridge_group_eht_set_entry,
297 rb_node);
298 result = memcmp(&eht_host->h_addr, &this->h_addr,
299 sizeof(union net_bridge_eht_addr));
300 parent = *link;
301 if (result < 0)
302 link = &((*link)->rb_left);
303 else if (result > 0)
304 link = &((*link)->rb_right);
305 else
306 return this;
307 }
308
309 /* always allow auto-created zero entry */
310 if (!allow_zero_src && eht_host->num_entries >= PG_SRC_ENT_LIMIT)
311 return NULL;
312
313 set_h = kzalloc(sizeof(*set_h), GFP_ATOMIC);
314 if (!set_h)
315 return NULL;
316
317 memcpy(&set_h->h_addr, &eht_host->h_addr,
318 sizeof(union net_bridge_eht_addr));
319 set_h->mcast_gc.destroy = br_multicast_destroy_eht_set_entry;
320 set_h->eht_set = eht_set;
321 set_h->h_parent = eht_host;
322 set_h->br = br;
323 timer_setup(&set_h->timer, br_multicast_eht_set_entry_expired, 0);
324
325 hlist_add_head(&set_h->host_list, &eht_host->set_entries);
326 rb_link_node(&set_h->rb_node, parent, link);
327 rb_insert_color(&set_h->rb_node, &eht_set->entry_tree);
328 /* we must not count the auto-created zero entry otherwise we won't be
329 * able to track the full list of PG_SRC_ENT_LIMIT entries
330 */
331 if (!allow_zero_src)
332 eht_host->num_entries++;
333
334 return set_h;
335}
336
337static struct net_bridge_group_eht_set *
338__eht_lookup_create_set(struct net_bridge_port_group *pg,
339 union net_bridge_eht_addr *src_addr)
340{
341 struct rb_node **link = &pg->eht_set_tree.rb_node, *parent = NULL;
342 struct net_bridge_group_eht_set *eht_set;
343
344 while (*link) {
345 struct net_bridge_group_eht_set *this;
346 int result;
347
348 this = rb_entry(*link, struct net_bridge_group_eht_set,
349 rb_node);
350 result = memcmp(src_addr, &this->src_addr, sizeof(*src_addr));
351 parent = *link;
352 if (result < 0)
353 link = &((*link)->rb_left);
354 else if (result > 0)
355 link = &((*link)->rb_right);
356 else
357 return this;
358 }
359
360 eht_set = kzalloc(sizeof(*eht_set), GFP_ATOMIC);
361 if (!eht_set)
362 return NULL;
363
364 memcpy(&eht_set->src_addr, src_addr, sizeof(*src_addr));
365 eht_set->mcast_gc.destroy = br_multicast_destroy_eht_set;
366 eht_set->pg = pg;
367 eht_set->br = pg->key.port->br;
368 eht_set->entry_tree = RB_ROOT;
369 timer_setup(&eht_set->timer, br_multicast_eht_set_expired, 0);
370
371 rb_link_node(&eht_set->rb_node, parent, link);
372 rb_insert_color(&eht_set->rb_node, &pg->eht_set_tree);
373
374 return eht_set;
375}
376
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200377static void br_multicast_ip_src_to_eht_addr(const struct br_ip *src,
378 union net_bridge_eht_addr *dest)
379{
380 switch (src->proto) {
381 case htons(ETH_P_IP):
382 dest->ip4 = src->src.ip4;
383 break;
384#if IS_ENABLED(CONFIG_IPV6)
385 case htons(ETH_P_IPV6):
386 memcpy(&dest->ip6, &src->src.ip6, sizeof(struct in6_addr));
387 break;
388#endif
389 }
390}
391
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300392static void br_eht_convert_host_filter_mode(const struct net_bridge_mcast *brmctx,
393 struct net_bridge_port_group *pg,
Nikolay Aleksandrovc9739012021-01-20 16:52:01 +0200394 union net_bridge_eht_addr *h_addr,
395 int filter_mode)
396{
397 struct net_bridge_group_eht_host *eht_host;
398 union net_bridge_eht_addr zero_addr;
399
400 eht_host = br_multicast_eht_host_lookup(pg, h_addr);
401 if (eht_host)
402 eht_host->filter_mode = filter_mode;
403
404 memset(&zero_addr, 0, sizeof(zero_addr));
405 switch (filter_mode) {
406 case MCAST_INCLUDE:
407 br_multicast_del_eht_set_entry(pg, &zero_addr, h_addr);
408 break;
409 case MCAST_EXCLUDE:
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300410 br_multicast_create_eht_set_entry(brmctx, pg, &zero_addr,
411 h_addr, MCAST_EXCLUDE,
Nikolay Aleksandrovc9739012021-01-20 16:52:01 +0200412 true);
413 break;
414 }
415}
416
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300417static void br_multicast_create_eht_set_entry(const struct net_bridge_mcast *brmctx,
418 struct net_bridge_port_group *pg,
Nikolay Aleksandrovbaa74d32021-01-20 16:51:56 +0200419 union net_bridge_eht_addr *src_addr,
420 union net_bridge_eht_addr *h_addr,
421 int filter_mode,
422 bool allow_zero_src)
423{
424 struct net_bridge_group_eht_set_entry *set_h;
425 struct net_bridge_group_eht_host *eht_host;
426 struct net_bridge *br = pg->key.port->br;
427 struct net_bridge_group_eht_set *eht_set;
428 union net_bridge_eht_addr zero_addr;
429
430 memset(&zero_addr, 0, sizeof(zero_addr));
431 if (!allow_zero_src && !memcmp(src_addr, &zero_addr, sizeof(zero_addr)))
432 return;
433
434 eht_set = __eht_lookup_create_set(pg, src_addr);
435 if (!eht_set)
436 return;
437
438 eht_host = __eht_lookup_create_host(pg, h_addr, filter_mode);
439 if (!eht_host)
440 goto fail_host;
441
442 set_h = __eht_lookup_create_set_entry(br, eht_set, eht_host,
443 allow_zero_src);
444 if (!set_h)
445 goto fail_set_entry;
446
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300447 mod_timer(&set_h->timer, jiffies + br_multicast_gmi(brmctx));
448 mod_timer(&eht_set->timer, jiffies + br_multicast_gmi(brmctx));
Nikolay Aleksandrovbaa74d32021-01-20 16:51:56 +0200449
450 return;
451
452fail_set_entry:
453 if (hlist_empty(&eht_host->set_entries))
454 __eht_destroy_host(eht_host);
455fail_host:
456 if (RB_EMPTY_ROOT(&eht_set->entry_tree))
457 br_multicast_del_eht_set(eht_set);
458}
459
460static bool br_multicast_del_eht_set_entry(struct net_bridge_port_group *pg,
461 union net_bridge_eht_addr *src_addr,
462 union net_bridge_eht_addr *h_addr)
463{
464 struct net_bridge_group_eht_set_entry *set_h;
465 struct net_bridge_group_eht_set *eht_set;
466 bool set_deleted = false;
467
468 eht_set = br_multicast_eht_set_lookup(pg, src_addr);
469 if (!eht_set)
470 goto out;
471
472 set_h = br_multicast_eht_set_entry_lookup(eht_set, h_addr);
473 if (!set_h)
474 goto out;
475
476 __eht_del_set_entry(set_h);
477
478 if (RB_EMPTY_ROOT(&eht_set->entry_tree)) {
479 br_multicast_del_eht_set(eht_set);
480 set_deleted = true;
481 }
482
483out:
484 return set_deleted;
485}
Nikolay Aleksandrovdba6b0a2021-01-20 16:51:57 +0200486
487static void br_multicast_del_eht_host(struct net_bridge_port_group *pg,
488 union net_bridge_eht_addr *h_addr)
489{
490 struct net_bridge_group_eht_set_entry *set_h;
491 struct net_bridge_group_eht_host *eht_host;
492 struct hlist_node *tmp;
493
494 eht_host = br_multicast_eht_host_lookup(pg, h_addr);
495 if (!eht_host)
496 return;
497
498 hlist_for_each_entry_safe(set_h, tmp, &eht_host->set_entries, host_list)
499 br_multicast_del_eht_set_entry(set_h->eht_set->pg,
500 &set_h->eht_set->src_addr,
501 &set_h->h_addr);
502}
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200503
Nikolay Aleksandrove09cf582021-03-15 19:13:42 +0200504/* create new set entries from reports */
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300505static void __eht_create_set_entries(const struct net_bridge_mcast *brmctx,
506 struct net_bridge_port_group *pg,
Nikolay Aleksandrove09cf582021-03-15 19:13:42 +0200507 union net_bridge_eht_addr *h_addr,
508 void *srcs,
509 u32 nsrcs,
510 size_t addr_size,
511 int filter_mode)
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200512{
513 union net_bridge_eht_addr eht_src_addr;
514 u32 src_idx;
515
516 memset(&eht_src_addr, 0, sizeof(eht_src_addr));
517 for (src_idx = 0; src_idx < nsrcs; src_idx++) {
518 memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300519 br_multicast_create_eht_set_entry(brmctx, pg, &eht_src_addr,
520 h_addr, filter_mode,
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200521 false);
522 }
523}
524
Nikolay Aleksandrove09cf582021-03-15 19:13:42 +0200525/* delete existing set entries and their (S,G) entries if they were the last */
526static bool __eht_del_set_entries(struct net_bridge_port_group *pg,
527 union net_bridge_eht_addr *h_addr,
528 void *srcs,
529 u32 nsrcs,
530 size_t addr_size)
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200531{
532 union net_bridge_eht_addr eht_src_addr;
533 struct net_bridge_group_src *src_ent;
534 bool changed = false;
535 struct br_ip src_ip;
536 u32 src_idx;
537
538 memset(&eht_src_addr, 0, sizeof(eht_src_addr));
539 memset(&src_ip, 0, sizeof(src_ip));
540 src_ip.proto = pg->key.addr.proto;
541 for (src_idx = 0; src_idx < nsrcs; src_idx++) {
542 memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
543 if (!br_multicast_del_eht_set_entry(pg, &eht_src_addr, h_addr))
544 continue;
545 memcpy(&src_ip, srcs + (src_idx * addr_size), addr_size);
546 src_ent = br_multicast_find_group_src(pg, &src_ip);
547 if (!src_ent)
548 continue;
Nikolay Aleksandrovd5a10222021-01-20 16:52:03 +0200549 br_multicast_del_group_src(src_ent, true);
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200550 changed = true;
551 }
552
553 return changed;
554}
555
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300556static bool br_multicast_eht_allow(const struct net_bridge_mcast *brmctx,
557 struct net_bridge_port_group *pg,
Nikolay Aleksandrove09cf582021-03-15 19:13:42 +0200558 union net_bridge_eht_addr *h_addr,
559 void *srcs,
560 u32 nsrcs,
561 size_t addr_size)
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200562{
Nikolay Aleksandrove09cf582021-03-15 19:13:42 +0200563 bool changed = false;
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200564
Nikolay Aleksandrove09cf582021-03-15 19:13:42 +0200565 switch (br_multicast_eht_host_filter_mode(pg, h_addr)) {
566 case MCAST_INCLUDE:
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300567 __eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs,
568 addr_size, MCAST_INCLUDE);
Nikolay Aleksandrove09cf582021-03-15 19:13:42 +0200569 break;
570 case MCAST_EXCLUDE:
571 changed = __eht_del_set_entries(pg, h_addr, srcs, nsrcs,
572 addr_size);
573 break;
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200574 }
Nikolay Aleksandrove09cf582021-03-15 19:13:42 +0200575
576 return changed;
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200577}
578
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300579static bool br_multicast_eht_block(const struct net_bridge_mcast *brmctx,
580 struct net_bridge_port_group *pg,
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200581 union net_bridge_eht_addr *h_addr,
582 void *srcs,
583 u32 nsrcs,
584 size_t addr_size)
585{
586 bool changed = false;
587
588 switch (br_multicast_eht_host_filter_mode(pg, h_addr)) {
589 case MCAST_INCLUDE:
Nikolay Aleksandrove09cf582021-03-15 19:13:42 +0200590 changed = __eht_del_set_entries(pg, h_addr, srcs, nsrcs,
591 addr_size);
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200592 break;
593 case MCAST_EXCLUDE:
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300594 __eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
Nikolay Aleksandrove09cf582021-03-15 19:13:42 +0200595 MCAST_EXCLUDE);
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200596 break;
597 }
598
599 return changed;
600}
601
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200602/* flush_entries is true when changing mode */
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300603static bool __eht_inc_exc(const struct net_bridge_mcast *brmctx,
604 struct net_bridge_port_group *pg,
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200605 union net_bridge_eht_addr *h_addr,
606 void *srcs,
607 u32 nsrcs,
608 size_t addr_size,
609 unsigned char filter_mode,
610 bool to_report)
611{
612 bool changed = false, flush_entries = to_report;
613 union net_bridge_eht_addr eht_src_addr;
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200614
615 if (br_multicast_eht_host_filter_mode(pg, h_addr) != filter_mode)
616 flush_entries = true;
617
618 memset(&eht_src_addr, 0, sizeof(eht_src_addr));
619 /* if we're changing mode del host and its entries */
620 if (flush_entries)
621 br_multicast_del_eht_host(pg, h_addr);
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300622 __eht_create_set_entries(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
Nikolay Aleksandrove09cf582021-03-15 19:13:42 +0200623 filter_mode);
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200624 /* we can be missing sets only if we've deleted some entries */
625 if (flush_entries) {
Nikolay Aleksandrovb66bf552021-01-20 16:52:00 +0200626 struct net_bridge_group_eht_set *eht_set;
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200627 struct net_bridge_group_src *src_ent;
628 struct hlist_node *tmp;
629
630 hlist_for_each_entry_safe(src_ent, tmp, &pg->src_list, node) {
631 br_multicast_ip_src_to_eht_addr(&src_ent->addr,
632 &eht_src_addr);
633 if (!br_multicast_eht_set_lookup(pg, &eht_src_addr)) {
Nikolay Aleksandrovd5a10222021-01-20 16:52:03 +0200634 br_multicast_del_group_src(src_ent, true);
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200635 changed = true;
636 continue;
637 }
Nikolay Aleksandrovb66bf552021-01-20 16:52:00 +0200638 /* this is an optimization for TO_INCLUDE where we lower
639 * the set's timeout to LMQT to catch timeout hosts:
640 * - host A (timing out): set entries X, Y
641 * - host B: set entry Z (new from current TO_INCLUDE)
642 * sends BLOCK Z after LMQT but host A's EHT
643 * entries still exist (unless lowered to LMQT
644 * so they can timeout with the S,Gs)
645 * => we wait another LMQT, when we can just delete the
646 * group immediately
647 */
648 if (!(src_ent->flags & BR_SGRP_F_SEND) ||
649 filter_mode != MCAST_INCLUDE ||
650 !to_report)
651 continue;
652 eht_set = br_multicast_eht_set_lookup(pg,
653 &eht_src_addr);
654 if (!eht_set)
655 continue;
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300656 mod_timer(&eht_set->timer, jiffies + br_multicast_lmqt(brmctx));
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200657 }
658 }
659
660 return changed;
661}
662
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300663static bool br_multicast_eht_inc(const struct net_bridge_mcast *brmctx,
664 struct net_bridge_port_group *pg,
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200665 union net_bridge_eht_addr *h_addr,
666 void *srcs,
667 u32 nsrcs,
668 size_t addr_size,
669 bool to_report)
670{
Nikolay Aleksandrovc9739012021-01-20 16:52:01 +0200671 bool changed;
672
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300673 changed = __eht_inc_exc(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
Nikolay Aleksandrovc9739012021-01-20 16:52:01 +0200674 MCAST_INCLUDE, to_report);
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300675 br_eht_convert_host_filter_mode(brmctx, pg, h_addr, MCAST_INCLUDE);
Nikolay Aleksandrovc9739012021-01-20 16:52:01 +0200676
677 return changed;
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200678}
679
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300680static bool br_multicast_eht_exc(const struct net_bridge_mcast *brmctx,
681 struct net_bridge_port_group *pg,
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200682 union net_bridge_eht_addr *h_addr,
683 void *srcs,
684 u32 nsrcs,
685 size_t addr_size,
686 bool to_report)
687{
Nikolay Aleksandrovc9739012021-01-20 16:52:01 +0200688 bool changed;
689
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300690 changed = __eht_inc_exc(brmctx, pg, h_addr, srcs, nsrcs, addr_size,
Nikolay Aleksandrovc9739012021-01-20 16:52:01 +0200691 MCAST_EXCLUDE, to_report);
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300692 br_eht_convert_host_filter_mode(brmctx, pg, h_addr, MCAST_EXCLUDE);
Nikolay Aleksandrovc9739012021-01-20 16:52:01 +0200693
694 return changed;
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200695}
696
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300697static bool __eht_ip4_handle(const struct net_bridge_mcast *brmctx,
698 struct net_bridge_port_group *pg,
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200699 union net_bridge_eht_addr *h_addr,
700 void *srcs,
701 u32 nsrcs,
702 int grec_type)
703{
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200704 bool changed = false, to_report = false;
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200705
706 switch (grec_type) {
707 case IGMPV3_ALLOW_NEW_SOURCES:
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300708 br_multicast_eht_allow(brmctx, pg, h_addr, srcs, nsrcs,
709 sizeof(__be32));
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200710 break;
711 case IGMPV3_BLOCK_OLD_SOURCES:
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300712 changed = br_multicast_eht_block(brmctx, pg, h_addr, srcs, nsrcs,
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200713 sizeof(__be32));
714 break;
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200715 case IGMPV3_CHANGE_TO_INCLUDE:
716 to_report = true;
717 fallthrough;
718 case IGMPV3_MODE_IS_INCLUDE:
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300719 changed = br_multicast_eht_inc(brmctx, pg, h_addr, srcs, nsrcs,
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200720 sizeof(__be32), to_report);
721 break;
722 case IGMPV3_CHANGE_TO_EXCLUDE:
723 to_report = true;
724 fallthrough;
725 case IGMPV3_MODE_IS_EXCLUDE:
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300726 changed = br_multicast_eht_exc(brmctx, pg, h_addr, srcs, nsrcs,
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200727 sizeof(__be32), to_report);
728 break;
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200729 }
730
731 return changed;
732}
733
734#if IS_ENABLED(CONFIG_IPV6)
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300735static bool __eht_ip6_handle(const struct net_bridge_mcast *brmctx,
736 struct net_bridge_port_group *pg,
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200737 union net_bridge_eht_addr *h_addr,
738 void *srcs,
739 u32 nsrcs,
740 int grec_type)
741{
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200742 bool changed = false, to_report = false;
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200743
744 switch (grec_type) {
745 case MLD2_ALLOW_NEW_SOURCES:
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300746 br_multicast_eht_allow(brmctx, pg, h_addr, srcs, nsrcs,
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200747 sizeof(struct in6_addr));
748 break;
749 case MLD2_BLOCK_OLD_SOURCES:
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300750 changed = br_multicast_eht_block(brmctx, pg, h_addr, srcs, nsrcs,
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200751 sizeof(struct in6_addr));
752 break;
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200753 case MLD2_CHANGE_TO_INCLUDE:
754 to_report = true;
755 fallthrough;
756 case MLD2_MODE_IS_INCLUDE:
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300757 changed = br_multicast_eht_inc(brmctx, pg, h_addr, srcs, nsrcs,
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200758 sizeof(struct in6_addr),
759 to_report);
760 break;
761 case MLD2_CHANGE_TO_EXCLUDE:
762 to_report = true;
763 fallthrough;
764 case MLD2_MODE_IS_EXCLUDE:
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300765 changed = br_multicast_eht_exc(brmctx, pg, h_addr, srcs, nsrcs,
Nikolay Aleksandrovddc255d2021-01-20 16:51:59 +0200766 sizeof(struct in6_addr),
767 to_report);
768 break;
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200769 }
770
771 return changed;
772}
773#endif
774
775/* true means an entry was deleted */
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300776bool br_multicast_eht_handle(const struct net_bridge_mcast *brmctx,
777 struct net_bridge_port_group *pg,
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200778 void *h_addr,
779 void *srcs,
780 u32 nsrcs,
781 size_t addr_size,
782 int grec_type)
783{
784 bool eht_enabled = !!(pg->key.port->flags & BR_MULTICAST_FAST_LEAVE);
785 union net_bridge_eht_addr eht_host_addr;
786 bool changed = false;
787
788 if (!eht_enabled)
789 goto out;
790
791 memset(&eht_host_addr, 0, sizeof(eht_host_addr));
792 memcpy(&eht_host_addr, h_addr, addr_size);
793 if (addr_size == sizeof(__be32))
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300794 changed = __eht_ip4_handle(brmctx, pg, &eht_host_addr, srcs,
795 nsrcs, grec_type);
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200796#if IS_ENABLED(CONFIG_IPV6)
797 else
Nikolay Aleksandrovadc47032021-07-19 20:06:25 +0300798 changed = __eht_ip6_handle(brmctx, pg, &eht_host_addr, srcs,
799 nsrcs, grec_type);
Nikolay Aleksandrov474ddb32021-01-20 16:51:58 +0200800#endif
801
802out:
803 return changed;
804}
Nikolay Aleksandrov2dba4072021-01-26 11:35:33 +0200805
806int br_multicast_eht_set_hosts_limit(struct net_bridge_port *p,
807 u32 eht_hosts_limit)
808{
809 struct net_bridge *br = p->br;
810
811 if (!eht_hosts_limit)
812 return -EINVAL;
813
814 spin_lock_bh(&br->multicast_lock);
815 p->multicast_eht_hosts_limit = eht_hosts_limit;
816 spin_unlock_bh(&br->multicast_lock);
817
818 return 0;
819}